]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Wed, 15 Jul 2026 00:11:25 +0000 (20:11 -0400)
committerSasha Levin <sashal@kernel.org>
Wed, 15 Jul 2026 00:11:25 +0000 (20:11 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
28 files changed:
queue-5.10/iio-adc-spear-initialize-completion-before-requestin.patch [new file with mode: 0644]
queue-5.10/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch [new file with mode: 0644]
queue-5.15/series
queue-6.1/efivarfs-expose-used-and-total-size.patch [new file with mode: 0644]
queue-6.1/efivarfs-fix-statfs-on-efivarfs.patch [new file with mode: 0644]
queue-6.1/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch [new file with mode: 0644]
queue-6.1/series
queue-6.12/pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch [new file with mode: 0644]
queue-6.12/series
queue-6.12/x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch [new file with mode: 0644]
queue-6.18/bpf-prefer-dirty-packs-for-ebpf-allocations.patch [new file with mode: 0644]
queue-6.18/bpf-prefer-packs-that-won-t-trigger-an-ibpb-flush-on.patch [new file with mode: 0644]
queue-6.18/bpf-restrict-jit-predictor-flush-to-cbpf.patch [new file with mode: 0644]
queue-6.18/bpf-skip-redundant-ibpb-in-pack-allocator.patch [new file with mode: 0644]
queue-6.18/bpf-support-for-hardening-against-jit-spraying.patch [new file with mode: 0644]
queue-6.18/mm-shmem-fix-potential-livelock-issue-for-shmem-dire.patch [new file with mode: 0644]
queue-6.18/rust_binder-fix-binder_get_extended_error.patch [new file with mode: 0644]
queue-6.18/rust_binder-introduce-transactioninfo.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/x86-bugs-enable-ibpb-flush-on-bpf-jit-allocation.patch [new file with mode: 0644]
queue-6.18/x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch [new file with mode: 0644]
queue-6.6/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch [new file with mode: 0644]
queue-6.6/pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch [new file with mode: 0644]
queue-6.6/series
queue-7.1/series
queue-7.1/wifi-mt76-mt7921-mt7925-fix-null-dereference-in-csa-.patch [new file with mode: 0644]

diff --git a/queue-5.10/iio-adc-spear-initialize-completion-before-requestin.patch b/queue-5.10/iio-adc-spear-initialize-completion-before-requestin.patch
new file mode 100644 (file)
index 0000000..07aa1af
--- /dev/null
@@ -0,0 +1,79 @@
+From bfdf815f1f6afd08fb56ac84af61e6389c0b455a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 11:58:33 -0500
+Subject: iio: adc: spear: Initialize completion before requesting IRQ
+
+From: Maxwell Doose <m32285159@gmail.com>
+
+[ Upstream commit 3ee2128b6f0eb0be7b6cb8f6e0f1f113a65201a0 ]
+
+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 <sangyun.kim@snu.ac.kr>
+Reported-by: Kyungwook Boo <bookyungwook@gmail.com>
+Reported-by: Jaeyoung Chung <jjy600901@snu.ac.kr>
+Closes: https://lore.kernel.org/linux-iio/20260610115700.774689-1-jjy600901@snu.ac.kr/
+Signed-off-by: Maxwell Doose <m32285159@gmail.com>
+Reviewed-by: Vladimir Zapolskiy <vz@kernel.org>
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Maxwell Doose <m32285159@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iio/adc/spear_adc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
+index 4d4aff88aa6ceb..12e0811a47b71a 100644
+--- a/drivers/iio/adc/spear_adc.c
++++ b/drivers/iio/adc/spear_adc.c
+@@ -274,6 +274,8 @@ static int spear_adc_probe(struct platform_device *pdev)
+       st = iio_priv(indio_dev);
+       st->np = np;
++      init_completion(&st->completion);
++
+       /*
+        * SPEAr600 has a different register layout than other SPEAr SoC's
+        * (e.g. SPEAr3xx). Let's provide two register base addresses
+@@ -334,8 +336,6 @@ static int spear_adc_probe(struct platform_device *pdev)
+       platform_set_drvdata(pdev, indio_dev);
+-      init_completion(&st->completion);
+-
+       indio_dev->name = SPEAR_ADC_MOD_NAME;
+       indio_dev->info = &spear_adc_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+-- 
+2.53.0
+
diff --git a/queue-5.10/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch b/queue-5.10/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch
new file mode 100644 (file)
index 0000000..57f5201
--- /dev/null
@@ -0,0 +1,45 @@
+From d030a71ea48cf8fee36476ebf9d736e14688c58f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:16:13 +0300
+Subject: iio: temperature: ltc2983: Fix n_wires default bypassing rotation
+ check
+
+From: Liviu Stan <liviu.stan@analog.com>
+
+[ Upstream commit 434c150752675f44dc52c384a7fa22e5176bc35a ]
+
+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 <liviu.stan@analog.com>
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Liviu Stan <liviu.stan@analog.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 ecc8b2e129b896..c6748cfc5d5ce2 100644
+--- a/drivers/iio/temperature/ltc2983.c
++++ b/drivers/iio/temperature/ltc2983.c
+@@ -697,7 +697,7 @@ static struct ltc2983_sensor *ltc2983_rtd_new(const struct device_node *child,
+       int ret = 0;
+       struct device *dev = &st->spi->dev;
+       struct device_node *phandle;
+-      u32 excitation_current = 0, n_wires = 0;
++      u32 excitation_current = 0, n_wires = 2;
+       rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
+       if (!rtd)
+-- 
+2.53.0
+
index 95ef10120af2215a3131f804ef7683de53023dea..078c4243e956ed43c3afb0b9ae2204648810b92a 100644 (file)
@@ -117,3 +117,5 @@ usbip-vudc-fix-null-deref-in-vep_dequeue.patch
 usb-typec-tcpm-validate-svid-index-in-svdm_consume_modes.patch
 usb-typec-ucsi-invert-displayport-role-assignment.patch
 usb-typec-ucsi-pass-full-dp-config-payload-in-set_new_cam-for-dp-alt-mode.patch
+iio-adc-spear-initialize-completion-before-requestin.patch
+iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch
diff --git a/queue-5.15/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch b/queue-5.15/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch
new file mode 100644 (file)
index 0000000..727eb90
--- /dev/null
@@ -0,0 +1,45 @@
+From b27b20bfecbb8408550e81b8d0101460a0bf1fce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:26:10 +0300
+Subject: iio: temperature: ltc2983: Fix n_wires default bypassing rotation
+ check
+
+From: Liviu Stan <liviu.stan@analog.com>
+
+[ Upstream commit 434c150752675f44dc52c384a7fa22e5176bc35a ]
+
+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 <liviu.stan@analog.com>
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Liviu Stan <liviu.stan@analog.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 ecc8b2e129b896..c6748cfc5d5ce2 100644
+--- a/drivers/iio/temperature/ltc2983.c
++++ b/drivers/iio/temperature/ltc2983.c
+@@ -697,7 +697,7 @@ static struct ltc2983_sensor *ltc2983_rtd_new(const struct device_node *child,
+       int ret = 0;
+       struct device *dev = &st->spi->dev;
+       struct device_node *phandle;
+-      u32 excitation_current = 0, n_wires = 0;
++      u32 excitation_current = 0, n_wires = 2;
+       rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
+       if (!rtd)
+-- 
+2.53.0
+
index 80abd42a4ed2642c971cab7c022ba907a59f719b..31b0edd6fe1b4c3cb9fa94d076cd9b4b14f0c492 100644 (file)
@@ -152,3 +152,4 @@ usbip-vudc-fix-null-deref-in-vep_dequeue.patch
 usb-typec-tcpm-validate-svid-index-in-svdm_consume_modes.patch
 usb-typec-ucsi-invert-displayport-role-assignment.patch
 usb-typec-ucsi-pass-full-dp-config-payload-in-set_new_cam-for-dp-alt-mode.patch
+iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch
diff --git a/queue-6.1/efivarfs-expose-used-and-total-size.patch b/queue-6.1/efivarfs-expose-used-and-total-size.patch
new file mode 100644 (file)
index 0000000..ca45dfd
--- /dev/null
@@ -0,0 +1,189 @@
+From 37936041684be30c92a2527b891c949bee970f0d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 23:33:29 -0500
+Subject: efivarfs: expose used and total size
+
+From: Anisse Astier <an.astier@criteo.com>
+
+[ Upstream commit d86ff3333cb1d5f42d8898fb5fdb304e143c0237 ]
+
+When writing EFI variables, one might get errors with no other message
+on why it fails. Being able to see how much is used by EFI variables
+helps analyzing such issues.
+
+Since this is not a conventional filesystem, block size is intentionally
+set to 1 instead of PAGE_SIZE.
+
+x86 quirks of reserved size are taken into account; so that available
+and free size can be different, further helping debugging space issues.
+
+With this patch, one can see the remaining space in EFI variable storage
+via efivarfs, like this:
+
+   $ df -h /sys/firmware/efi/efivars/
+   Filesystem      Size  Used Avail Use% Mounted on
+   efivarfs        176K  106K   66K  62% /sys/firmware/efi/efivars
+
+Signed-off-by: Anisse Astier <an.astier@criteo.com>
+[ardb: - rename efi_reserved_space() to efivar_reserved_space()
+       - whitespace/coding style tweaks]
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Adjusted for headers in linux-6.1.y
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/platform/efi/quirks.c |  8 +++++++
+ drivers/firmware/efi/efi.c     |  1 +
+ drivers/firmware/efi/vars.c    | 12 +++++++++++
+ fs/efivarfs/super.c            | 39 +++++++++++++++++++++++++++++++++-
+ include/linux/efi.h            | 11 ++++++++++
+ 5 files changed, 70 insertions(+), 1 deletion(-)
+
+diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c
+index b0d0376940ba8b..a29efaf6fe411e 100644
+--- a/arch/x86/platform/efi/quirks.c
++++ b/arch/x86/platform/efi/quirks.c
+@@ -114,6 +114,14 @@ void efi_delete_dummy_variable(void)
+                                    EFI_VARIABLE_RUNTIME_ACCESS, 0, NULL);
+ }
++u64 efivar_reserved_space(void)
++{
++      if (efi_no_storage_paranoia)
++              return 0;
++      return EFI_MIN_RESERVE;
++}
++EXPORT_SYMBOL_GPL(efivar_reserved_space);
++
+ /*
+  * In the nonblocking case we do not attempt to perform garbage
+  * collection if we do not have enough free space. Rather, we do the
+diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
+index 9786dfff9f840f..83b63dd2de9be3 100644
+--- a/drivers/firmware/efi/efi.c
++++ b/drivers/firmware/efi/efi.c
+@@ -211,6 +211,7 @@ static int generic_ops_register(void)
+       generic_ops.get_variable = efi.get_variable;
+       generic_ops.get_next_variable = efi.get_next_variable;
+       generic_ops.query_variable_store = efi_query_variable_store;
++      generic_ops.query_variable_info = efi.query_variable_info;
+       if (efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) {
+               generic_ops.set_variable = efi.set_variable;
+diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
+index 4ca256bcd69715..d22160e7c7851b 100644
+--- a/drivers/firmware/efi/vars.c
++++ b/drivers/firmware/efi/vars.c
+@@ -250,3 +250,15 @@ efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
+       return status;
+ }
+ EXPORT_SYMBOL_NS_GPL(efivar_set_variable, EFIVAR);
++
++efi_status_t efivar_query_variable_info(u32 attr,
++                                      u64 *storage_space,
++                                      u64 *remaining_space,
++                                      u64 *max_variable_size)
++{
++      if (!__efivars->ops->query_variable_info)
++              return EFI_UNSUPPORTED;
++      return __efivars->ops->query_variable_info(attr, storage_space,
++                      remaining_space, max_variable_size);
++}
++EXPORT_SYMBOL_NS_GPL(efivar_query_variable_info, EFIVAR);
+diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
+index 9025430cf2ad35..3fbced52fc6bb8 100644
+--- a/fs/efivarfs/super.c
++++ b/fs/efivarfs/super.c
+@@ -14,6 +14,7 @@
+ #include <linux/slab.h>
+ #include <linux/magic.h>
+ #include <linux/printk.h>
++#include <linux/statfs.h>
+ #include "internal.h"
+@@ -24,8 +25,44 @@ static void efivarfs_evict_inode(struct inode *inode)
+       clear_inode(inode);
+ }
++static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf)
++{
++      const u32 attr = EFI_VARIABLE_NON_VOLATILE |
++                       EFI_VARIABLE_BOOTSERVICE_ACCESS |
++                       EFI_VARIABLE_RUNTIME_ACCESS;
++      u64 storage_space, remaining_space, max_variable_size;
++      efi_status_t status;
++
++      status = efivar_query_variable_info(attr, &storage_space, &remaining_space,
++                                          &max_variable_size);
++      if (status != EFI_SUCCESS)
++              return efi_status_to_err(status);
++
++      /*
++       * This is not a normal filesystem, so no point in pretending it has a block
++       * size; we declare f_bsize to 1, so that we can then report the exact value
++       * sent by EFI QueryVariableInfo in f_blocks and f_bfree
++       */
++      buf->f_bsize    = 1;
++      buf->f_namelen  = NAME_MAX;
++      buf->f_blocks   = storage_space;
++      buf->f_bfree    = remaining_space;
++      buf->f_type     = dentry->d_sb->s_magic;
++
++      /*
++       * In f_bavail we declare the free space that the kernel will allow writing
++       * when the storage_paranoia x86 quirk is active. To use more, users
++       * should boot the kernel with efi_no_storage_paranoia.
++       */
++      if (remaining_space > efivar_reserved_space())
++              buf->f_bavail = remaining_space - efivar_reserved_space();
++      else
++              buf->f_bavail = 0;
++
++      return 0;
++}
+ static const struct super_operations efivarfs_ops = {
+-      .statfs = simple_statfs,
++      .statfs = efivarfs_statfs,
+       .drop_inode = generic_delete_inode,
+       .evict_inode = efivarfs_evict_inode,
+ };
+diff --git a/include/linux/efi.h b/include/linux/efi.h
+index 1d73c77051c09b..5868c4b2933261 100644
+--- a/include/linux/efi.h
++++ b/include/linux/efi.h
+@@ -1039,6 +1039,7 @@ struct efivar_operations {
+       efi_set_variable_t *set_variable;
+       efi_set_variable_t *set_variable_nonblocking;
+       efi_query_variable_store_t *query_variable_store;
++      efi_query_variable_info_t *query_variable_info;
+ };
+ struct efivars {
+@@ -1047,6 +1048,12 @@ struct efivars {
+       const struct efivar_operations *ops;
+ };
++#ifdef CONFIG_X86
++u64 __attribute_const__ efivar_reserved_space(void);
++#else
++static inline u64 efivar_reserved_space(void) { return 0; }
++#endif
++
+ /*
+  * The maximum size of VariableName + Data = 1024
+  * Therefore, it's reasonable to save that much
+@@ -1081,6 +1088,10 @@ efi_status_t efivar_set_variable_locked(efi_char16_t *name, efi_guid_t *vendor,
+ efi_status_t efivar_set_variable(efi_char16_t *name, efi_guid_t *vendor,
+                                u32 attr, unsigned long data_size, void *data);
++efi_status_t efivar_query_variable_info(u32 attr, u64 *storage_space,
++                                      u64 *remaining_space,
++                                      u64 *max_variable_size);
++
+ #if IS_ENABLED(CONFIG_EFI_CAPSULE_LOADER)
+ extern bool efi_capsule_pending(int *reset_type);
+-- 
+2.53.0
+
diff --git a/queue-6.1/efivarfs-fix-statfs-on-efivarfs.patch b/queue-6.1/efivarfs-fix-statfs-on-efivarfs.patch
new file mode 100644 (file)
index 0000000..525b030
--- /dev/null
@@ -0,0 +1,57 @@
+From 2da59d960269acedf14bd7729a5ac4e558624517 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 10 Sep 2023 06:54:45 +0200
+Subject: efivarfs: fix statfs() on efivarfs
+
+From: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
+
+[ Upstream commit 79b83606abc778aa3cbee535b362ce905d0b9448 ]
+
+Some firmware (notably U-Boot) provides GetVariable() and
+GetNextVariableName() but not QueryVariableInfo().
+
+With commit d86ff3333cb1 ("efivarfs: expose used and total size") the
+statfs syscall was broken for such firmware.
+
+If QueryVariableInfo() does not exist or returns EFI_UNSUPPORTED, just
+report the file system size as 0 as statfs_simple() previously did.
+
+Fixes: d86ff3333cb1 ("efivarfs: expose used and total size")
+Link: https://lore.kernel.org/all/20230910045445.41632-1-heinrich.schuchardt@canonical.com/
+Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
+[ardb: log warning on QueryVariableInfo() failure]
+Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/efivarfs/super.c | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
+index 3fbced52fc6bb8..53d19ecc4385fa 100644
+--- a/fs/efivarfs/super.c
++++ b/fs/efivarfs/super.c
+@@ -33,10 +33,16 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+       u64 storage_space, remaining_space, max_variable_size;
+       efi_status_t status;
+-      status = efivar_query_variable_info(attr, &storage_space, &remaining_space,
+-                                          &max_variable_size);
+-      if (status != EFI_SUCCESS)
+-              return efi_status_to_err(status);
++      /* Some UEFI firmware does not implement QueryVariableInfo() */
++      storage_space = remaining_space = 0;
++      if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) {
++              status = efivar_query_variable_info(attr, &storage_space,
++                                                  &remaining_space,
++                                                  &max_variable_size);
++              if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED)
++                      pr_warn_ratelimited("query_variable_info() failed: 0x%lx\n",
++                                          status);
++      }
+       /*
+        * This is not a normal filesystem, so no point in pretending it has a block
+-- 
+2.53.0
+
diff --git a/queue-6.1/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch b/queue-6.1/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch
new file mode 100644 (file)
index 0000000..582710e
--- /dev/null
@@ -0,0 +1,49 @@
+From f944be57e55f19a7e4b7329f2f9c9ddef780128a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:34:41 +0300
+Subject: iio: temperature: ltc2983: Fix n_wires default bypassing rotation
+ check
+
+From: Liviu Stan <liviu.stan@analog.com>
+
+[ Upstream commit 434c150752675f44dc52c384a7fa22e5176bc35a ]
+
+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 <liviu.stan@analog.com>
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Liviu Stan <liviu.stan@analog.com>
+
+[ Liviu Stan: resolve conflict - keep fwnode_handle *ref declaration
+  which was removed upstream by a later refactor not in 6.1.y ]
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 8d18823be40f41..9d3fb988cd061a 100644
+--- a/drivers/iio/temperature/ltc2983.c
++++ b/drivers/iio/temperature/ltc2983.c
+@@ -706,7 +706,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
+       int ret = 0;
+       struct device *dev = &st->spi->dev;
+       struct fwnode_handle *ref;
+-      u32 excitation_current = 0, n_wires = 0;
++      u32 excitation_current = 0, n_wires = 2;
+       rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
+       if (!rtd)
+-- 
+2.53.0
+
index 8abc47d8a8494879029c0d7a76597614a4caaf92..ad8eece94061f086ee5698e0b54d3a9766c9b97a 100644 (file)
@@ -194,3 +194,6 @@ usb-typec-ucsi-invert-displayport-role-assignment.patch
 usb-typec-ucsi-pass-full-dp-config-payload-in-set_new_cam-for-dp-alt-mode.patch
 usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch
 usb-typec-ucsi-cancel-pending-work-on-system-suspend.patch
+iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch
+efivarfs-expose-used-and-total-size.patch
+efivarfs-fix-statfs-on-efivarfs.patch
diff --git a/queue-6.12/pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch b/queue-6.12/pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch
new file mode 100644 (file)
index 0000000..ee337ab
--- /dev/null
@@ -0,0 +1,134 @@
+From 67f5da61ba39b5a5dac82a49d4623ac759a7a2f1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 23:25:49 +0100
+Subject: PCI: Always lift 2.5GT/s restriction in PCIe failed link retraining
+
+From: Maciej W. Rozycki <macro@orcam.me.uk>
+
+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 <macro@orcam.me.uk>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alok Tiwari <alok.a.tiwari@oracle.com>
+Cc: stable@vger.kernel.org # v6.5+
+Link: https://patch.msgid.link/alpine.DEB.2.21.2512080331530.49654@angie.orcam.me.uk
+[ Update for missing PCIe link speed helpers for 6.12.y. ]
+Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 51 +++++++++++++++++---------------------------
+ 1 file changed, 20 insertions(+), 31 deletions(-)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 3d05ea35c536f5..9b2acf388f8ad7 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -68,11 +68,10 @@
+  * 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
+@@ -83,12 +82,9 @@
+  */
+ 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)
+@@ -96,10 +92,9 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
+       pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2);
+       pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
++      oldlnkctl2 = lnkctl2;
+       if ((lnksta & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_DLLLA)) ==
+           PCI_EXP_LNKSTA_LBMS) {
+-              u16 oldlnkctl2 = lnkctl2;
+-
+               pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n");
+               lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
+@@ -107,35 +102,29 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
+               pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2);
+               ret = pcie_retrain_link(dev, false);
+-              if (ret) {
+-                      pci_info(dev, "retraining failed\n");
+-                      pcie_capability_write_word(dev, PCI_EXP_LNKCTL2,
+-                                                 oldlnkctl2);
+-                      pcie_retrain_link(dev, true);
+-                      return ret;
+-              }
+-
+-              pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
++              if (ret)
++                      goto err;
+       }
+-      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);
+               lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
+               lnkctl2 |= lnkcap & PCI_EXP_LNKCAP_SLS;
+               pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2);
+               ret = pcie_retrain_link(dev, 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_capability_write_word(dev, PCI_EXP_LNKCTL2, oldlnkctl2);
++
++      pcie_retrain_link(dev, true);
+       return ret;
+ }
+-- 
+2.53.0
+
index c9dc06c2aeca1f2124ac23acbac02b593e41ea33..5fad36f31bf5c719015f1b9c6af4c6b662f2ec15 100644 (file)
@@ -252,3 +252,5 @@ usb-typec-ucsi-pass-full-dp-config-payload-in-set_new_cam-for-dp-alt-mode.patch
 usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch
 usb-typec-ucsi-cancel-pending-work-on-system-suspend.patch
 usb-gadget-f_fs-fix-dma-fence-leak.patch
+x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch
+pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch
diff --git a/queue-6.12/x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch b/queue-6.12/x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch
new file mode 100644 (file)
index 0000000..f53a021
--- /dev/null
@@ -0,0 +1,129 @@
+From 6d17092213afa16c6188165aa5619c2a69e9f530 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 21:12:36 -0700
+Subject: x86,fs/resctrl: Prevent out-of-bounds access while offlining CPU when
+ SNC enabled
+
+From: Reinette Chatre <reinette.chatre@intel.com>
+
+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 <sashiko-bot@kernel.org>
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Cc: <stable@kernel.org>
+Link: https://patch.msgid.link/16137433df42f85013b2f7a53626795cbd6637b9.1781029125.git.reinette.chatre@intel.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/cpu/resctrl/monitor.c | 47 +++++++++++++++++----------
+ 1 file changed, 29 insertions(+), 18 deletions(-)
+
+diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c
+index c117fba4f8da92..b4dcfe6d0831ac 100644
+--- a/arch/x86/kernel/cpu/resctrl/monitor.c
++++ b/arch/x86/kernel/cpu/resctrl/monitor.c
+@@ -338,14 +338,20 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d,
+                          u64 *val, void *ignored)
+ {
+       struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d);
+-      int cpu = cpumask_any(&d->hdr.cpu_mask);
+       struct arch_mbm_state *am;
+       u64 msr_val;
+       u32 prmid;
++      int cpu;
+       int ret;
+       resctrl_arch_rmid_read_context_check();
++      if (cpumask_empty(&d->hdr.cpu_mask)) {
++              pr_warn_once("Domain %d has no CPUs\n", d->hdr.id);
++              return -EINVAL;
++      }
++
++      cpu = cpumask_any(&d->hdr.cpu_mask);
+       prmid = logical_rmid_to_physical_rmid(cpu, rmid);
+       ret = __rmid_read_phys(prmid, eventid, &msr_val);
+@@ -382,9 +388,9 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free)
+       struct rdt_resource *r = &rdt_resources_all[RDT_RESOURCE_L3].r_resctrl;
+       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;
+-      bool rmid_dirty;
+       u64 val = 0;
+       arch_mon_ctx = resctrl_arch_mon_ctx_alloc(r, QOS_L3_OCCUP_EVENT_ID);
+@@ -406,22 +412,27 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free)
+                       break;
+               entry = __rmid_entry(idx);
+-              if (resctrl_arch_rmid_read(r, d, entry->closid, entry->rmid,
+-                                         QOS_L3_OCCUP_EVENT_ID, &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, entry->closid,
++                                                 entry->rmid, QOS_L3_OCCUP_EVENT_ID,
++                                                 &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) {
+-- 
+2.53.0
+
diff --git a/queue-6.18/bpf-prefer-dirty-packs-for-ebpf-allocations.patch b/queue-6.18/bpf-prefer-dirty-packs-for-ebpf-allocations.patch
new file mode 100644 (file)
index 0000000..978cb87
--- /dev/null
@@ -0,0 +1,49 @@
+From 682abc7bee5723cea5d781b6645259dbf973f82c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:12:53 -0700
+Subject: bpf: Prefer dirty packs for eBPF allocations
+
+From: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
+
+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 <pawan.kumar.gupta@linux.intel.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 e0ffdd985eb5d7..b102704f89bc77 100644
+--- a/kernel/bpf/core.c
++++ b/kernel/bpf/core.c
+@@ -970,10 +970,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;
+-- 
+2.53.0
+
diff --git a/queue-6.18/bpf-prefer-packs-that-won-t-trigger-an-ibpb-flush-on.patch b/queue-6.18/bpf-prefer-packs-that-won-t-trigger-an-ibpb-flush-on.patch
new file mode 100644 (file)
index 0000000..e97e2f3
--- /dev/null
@@ -0,0 +1,88 @@
+From 07ecae528d2d2edf01835b1535bd227285872dfb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:12:38 -0700
+Subject: bpf: Prefer packs that won't trigger an IBPB flush on allocation
+
+From: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
+
+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 <pawan.kumar.gupta@linux.intel.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 49ca543a41115a..e0ffdd985eb5d7 100644
+--- a/kernel/bpf/core.c
++++ b/kernel/bpf/core.c
+@@ -930,8 +930,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);
+@@ -963,8 +963,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);
+-- 
+2.53.0
+
diff --git a/queue-6.18/bpf-restrict-jit-predictor-flush-to-cbpf.patch b/queue-6.18/bpf-restrict-jit-predictor-flush-to-cbpf.patch
new file mode 100644 (file)
index 0000000..e46b167
--- /dev/null
@@ -0,0 +1,236 @@
+From d73e19d7bddfdf4bc77edb2596952637f02308c1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:12:07 -0700
+Subject: bpf: Restrict JIT predictor flush to cBPF
+
+From: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
+
+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.
+
+  [pawan: backport had various conflicts in arches bpf_int_jit_compile().
+         loongarch bpf_int_jit_compile() doesn't use pack allocator,
+         dropped was_classic hunk. ]
+
+Signed-off-by: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/net/bpf_jit_comp.c   |  4 ++--
+ arch/loongarch/net/bpf_jit.c    |  2 +-
+ 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, 23 insertions(+), 17 deletions(-)
+
+diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
+index 873c1b784a872d..c563bae8fce1c2 100644
+--- a/arch/arm64/net/bpf_jit_comp.c
++++ b/arch/arm64/net/bpf_jit_comp.c
+@@ -2117,7 +2117,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+       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) {
+               prog = orig_prog;
+               goto out_off;
+@@ -2754,7 +2754,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 e9d666508ae28f..a000620ca99da7 100644
+--- a/arch/loongarch/net/bpf_jit.c
++++ b/arch/loongarch/net/bpf_jit.c
+@@ -1474,7 +1474,7 @@ static void invoke_bpf_mod_ret(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)
+diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
+index 189ef7b72081d2..cedab25aa3a823 100644
+--- a/arch/powerpc/net/bpf_jit_comp.c
++++ b/arch/powerpc/net/bpf_jit_comp.c
+@@ -246,7 +246,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *fp)
+       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) {
+               fp = org_fp;
+               goto out_addrs;
+@@ -468,7 +468,7 @@ bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)
+ 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 45cbc7c6fe490c..9e9e6dcfc48254 100644
+--- a/arch/riscv/net/bpf_jit_comp64.c
++++ b/arch/riscv/net/bpf_jit_comp64.c
+@@ -1268,7 +1268,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 191bf0e66c8243..e4ab5bb9c9f646 100644
+--- a/arch/riscv/net/bpf_jit_core.c
++++ b/arch/riscv/net/bpf_jit_core.c
+@@ -125,7 +125,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+                               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) {
+                               prog = orig_prog;
+                               goto out_offset;
+diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
+index 788671a32d8ee0..c3798cab3b7db0 100644
+--- a/arch/x86/net/bpf_jit_comp.c
++++ b/arch/x86/net/bpf_jit_comp.c
+@@ -3447,7 +3447,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)
+@@ -3780,7 +3780,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
+                       /* 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) {
+                               prog = orig_prog;
+                               goto out_addrs;
+diff --git a/include/linux/filter.h b/include/linux/filter.h
+index 3c4dd718beced8..2469fd2e401578 100644
+--- a/include/linux/filter.h
++++ b/include/linux/filter.h
+@@ -1290,7 +1290,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)
+@@ -1304,7 +1304,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 a043fccb917e86..338a806e25c045 100644
+--- a/kernel/bpf/core.c
++++ b/kernel/bpf/core.c
+@@ -924,7 +924,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;
+@@ -939,7 +939,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);
+@@ -971,7 +971,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);
+@@ -1131,7 +1133,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;
+@@ -1144,7 +1147,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 b77db7413f8c70..ea2d60dc1feeb7 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);
+-- 
+2.53.0
+
diff --git a/queue-6.18/bpf-skip-redundant-ibpb-in-pack-allocator.patch b/queue-6.18/bpf-skip-redundant-ibpb-in-pack-allocator.patch
new file mode 100644 (file)
index 0000000..80a8299
--- /dev/null
@@ -0,0 +1,81 @@
+From 17fa250049b03cbe5bff7f5fadb7622fb3d3c790 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:12:23 -0700
+Subject: bpf: Skip redundant IBPB in pack allocator
+
+From: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
+
+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 <pawan.kumar.gupta@linux.intel.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 338a806e25c045..49ca543a41115a 100644
+--- a/kernel/bpf/core.c
++++ b/kernel/bpf/core.c
+@@ -857,6 +857,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[];
+ };
+@@ -910,6 +911,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);
+@@ -972,8 +975,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);
+@@ -1011,6 +1021,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);
+-- 
+2.53.0
+
diff --git a/queue-6.18/bpf-support-for-hardening-against-jit-spraying.patch b/queue-6.18/bpf-support-for-hardening-against-jit-spraying.patch
new file mode 100644 (file)
index 0000000..6b1954c
--- /dev/null
@@ -0,0 +1,120 @@
+From f8258f4ab04d2a59f347a2412743bf79669faef9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:11:37 -0700
+Subject: bpf: Support for hardening against JIT spraying
+
+From: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
+
+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 <pawan.kumar.gupta@linux.intel.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 cf7a0bce1bb6d8..3c4dd718beced8 100644
+--- a/include/linux/filter.h
++++ b/include/linux/filter.h
+@@ -22,6 +22,7 @@
+ #include <linux/vmalloc.h>
+ #include <linux/sockptr.h>
+ #include <crypto/sha1.h>
++#include <linux/static_call.h>
+ #include <linux/u64_stats_sync.h>
+ #include <net/sch_generic.h>
+@@ -1266,6 +1267,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 931a4ddd8530ce..a043fccb917e86 100644
+--- a/kernel/bpf/core.c
++++ b/kernel/bpf/core.c
+@@ -21,6 +21,7 @@
+ #include <crypto/sha1.h>
+ #include <linux/filter.h>
+ #include <linux/skbuff.h>
++#include <linux/static_call.h>
+ #include <linux/vmalloc.h>
+ #include <linux/prandom.h>
+ #include <linux/bpf.h>
+@@ -864,6 +865,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);
+@@ -923,6 +933,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) {
+@@ -953,6 +971,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);
+-- 
+2.53.0
+
diff --git a/queue-6.18/mm-shmem-fix-potential-livelock-issue-for-shmem-dire.patch b/queue-6.18/mm-shmem-fix-potential-livelock-issue-for-shmem-dire.patch
new file mode 100644 (file)
index 0000000..13565ac
--- /dev/null
@@ -0,0 +1,99 @@
+From 4703c3acf6204021d4625799da6b8f78c6fae7ff Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 10:09:22 +0800
+Subject: mm: shmem: fix potential livelock issue for shmem direct swapin
+
+From: Baolin Wang <baolin.wang@linux.alibaba.com>
+
+When skipping swapcache for synchronous IO swap devices, swapcache_prepare()
+is used to prevent parallel swapin from proceeding with the swap cache flag.
+However, on PREEMPT kernels this can lead to a livelock, as reported by Chao[1]:
+
+Thread A starts direct swapin of a shmem folio and calls swapcache_prepare()
+to set SWAP_HAS_CACHE. It may then be preempted inside workingset_refault().
+Meanwhile, a higher priority thread B also attempts direct swapin of the same
+shmem swap entry. Since swapcache_prepare() already marks the entry, thread B
+repeatedly gets -EEXIST and busy-loops waiting for thread A to finish. But as
+thread B runs at higher priority, thread A cannot preempt it, resulting in
+starvation and a livelock.
+
+Fix it by yielding the CPU with schedule_timeout_uninterruptible(1) when
+swapcache_prepare() fails, following the same approach used in commit
+029c4628b2eb ("mm: swap: get rid of livelock in swapin readahead") and
+commit 13ddaf26be32 ("mm/swap: fix race when skipping swapcache").
+
+However, commit 01626a1823 ("mm: avoid unconditional one-tick sleep when
+swapcache_prepare fails") found that the unconditional one-tick sleep can
+cause UI stuttering on latency-sensitive Android devices. So we can follow
+the same approach by adding a waitqueue to wake up tasks when needed,
+instead of always sleeping for a full tick.
+
+Note that mainline does not have this potential issue, which has already been
+resolved by Kairui's swap refactoring work[2].
+
+[1] https://lore.kernel.org/all/700a2cbf90a2484f979aac858f08f5d4@xiaomi.com/
+[2] https://lore.kernel.org/all/20260517-swap-table-p4-v5-0-88ae43e064c7@tencent.com/
+Fixes: 1dd44c0af4fa ("mm: shmem: skip swapcache for swapin of synchronous swap device")
+Reported-by: Ma Chao <machao26@xiaomi.com>
+Closes: https://lore.kernel.org/all/700a2cbf90a2484f979aac858f08f5d4@xiaomi.com/
+Signed-off-by: Baolin Wang <baolin.wang@linux.alibaba.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/shmem.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/mm/shmem.c b/mm/shmem.c
+index 94c5b0d78ac31e..3c329b794ae4df 100644
+--- a/mm/shmem.c
++++ b/mm/shmem.c
+@@ -2005,11 +2005,14 @@ static struct folio *shmem_alloc_and_add_folio(struct vm_fault *vmf,
+       return ERR_PTR(error);
+ }
++static DECLARE_WAIT_QUEUE_HEAD(shmem_swapcache_wq);
++
+ static struct folio *shmem_swap_alloc_folio(struct inode *inode,
+               struct vm_area_struct *vma, pgoff_t index,
+               swp_entry_t entry, int order, gfp_t gfp)
+ {
+       struct shmem_inode_info *info = SHMEM_I(inode);
++      DECLARE_WAITQUEUE(wait, current);
+       int nr_pages = 1 << order;
+       struct folio *new;
+       gfp_t alloc_gfp;
+@@ -2066,6 +2069,10 @@ static struct folio *shmem_swap_alloc_folio(struct inode *inode,
+       if (swapcache_prepare(entry, nr_pages)) {
+               folio_put(new);
+               new = ERR_PTR(-EEXIST);
++              /* Relax a bit to prevent rapid repeated page faults */
++              add_wait_queue(&shmem_swapcache_wq, &wait);
++              schedule_timeout_uninterruptible(1);
++              remove_wait_queue(&shmem_swapcache_wq, &wait);
+               /* Try smaller folio to avoid cache conflict */
+               goto fallback;
+       }
+@@ -2423,6 +2430,8 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
+       if (skip_swapcache) {
+               folio->swap.val = 0;
+               swapcache_clear(si, swap, nr_pages);
++              if (waitqueue_active(&shmem_swapcache_wq))
++                      wake_up(&shmem_swapcache_wq);
+       } else {
+               swap_cache_del_folio(folio);
+       }
+@@ -2442,8 +2451,11 @@ static int shmem_swapin_folio(struct inode *inode, pgoff_t index,
+       if (folio)
+               folio_unlock(folio);
+ failed_nolock:
+-      if (skip_swapcache)
++      if (skip_swapcache) {
+               swapcache_clear(si, folio->swap, folio_nr_pages(folio));
++              if (waitqueue_active(&shmem_swapcache_wq))
++                      wake_up(&shmem_swapcache_wq);
++      }
+       if (folio)
+               folio_put(folio);
+       put_swap_device(si);
+-- 
+2.53.0
+
diff --git a/queue-6.18/rust_binder-fix-binder_get_extended_error.patch b/queue-6.18/rust_binder-fix-binder_get_extended_error.patch
new file mode 100644 (file)
index 0000000..2251418
--- /dev/null
@@ -0,0 +1,287 @@
+From b834619c38e13ccf912fe057c0127e3a7da0faf4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 06:45:54 +0000
+Subject: rust_binder: fix BINDER_GET_EXTENDED_ERROR
+
+From: Alice Ryhl <aliceryhl@google.com>
+
+[ Upstream commit 77bfebf110773f5a0d6b5ff8110896adb2c9c335 ]
+
+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 <stable@kernel.org>
+Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver")
+Signed-off-by: Alice Ryhl <aliceryhl@google.com>
+Acked-by: Carlos Llamas <cmllamas@google.com>
+Link: https://patch.msgid.link/20260605-set-extended-error-v3-1-d60b69a75f97@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Alice Ryhl <aliceryhl@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 c6a834071f8cef..fd9e9d9e5658b1 100644
+--- a/drivers/android/binder/error.rs
++++ b/drivers/android/binder/error.rs
+@@ -72,20 +72,17 @@ impl core::fmt::Debug for BinderError {
+     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::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 93aa98c2a173a0..ad8bdf762ddc6f 100644
+--- a/drivers/android/binder/thread.rs
++++ b/drivers/android/binder/thread.rs
+@@ -498,9 +498,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(())
+     }
+@@ -1105,7 +1112,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;
+             }
+@@ -1117,8 +1127,9 @@ impl Thread {
+         &self,
+         reply: Result<DLArc<Transaction>, u32>,
+         transaction: &DArc<Transaction>,
++        extended_error: Option<ExtendedError>,
+     ) {
+-        if self.deliver_single_reply(reply, transaction) {
++        if self.deliver_single_reply(reply, transaction, extended_error) {
+             transaction.from.unwind_transaction_stack();
+         }
+     }
+@@ -1132,6 +1143,7 @@ impl Thread {
+         &self,
+         reply: Result<DLArc<Transaction>, u32>,
+         transaction: &DArc<Transaction>,
++        extended_error: Option<ExtendedError>,
+     ) -> bool {
+         if let Ok(transaction) = &reply {
+             transaction.set_outstanding(&mut self.process.inner.lock());
+@@ -1147,6 +1159,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);
+@@ -1217,6 +1235,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(())
+     }
+@@ -1225,6 +1246,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() {
+@@ -1234,23 +1257,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
+@@ -1315,18 +1336,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 733f452d8f49dd..b5434a2ae8193c 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 {
+@@ -82,7 +83,6 @@ impl Transaction {
+         from: &Arc<Thread>,
+         info: &mut TransactionInfo,
+     ) -> BinderResult<DLArc<Self>> {
+-        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 };
+@@ -90,7 +90,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(),
+         ) {
+@@ -117,7 +117,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: from.process.task.euid(),
+@@ -141,9 +141,8 @@ impl Transaction {
+         info: &mut TransactionInfo,
+         allow_fds: bool,
+     ) -> BinderResult<DLArc<Self>> {
+-        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);
+@@ -154,7 +153,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: from.process.task.euid(),
+@@ -380,7 +379,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();
+         });
+@@ -462,7 +461,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();
+-- 
+2.53.0
+
diff --git a/queue-6.18/rust_binder-introduce-transactioninfo.patch b/queue-6.18/rust_binder-introduce-transactioninfo.patch
new file mode 100644 (file)
index 0000000..bf3337e
--- /dev/null
@@ -0,0 +1,668 @@
+From 33e07cce06e5933bfa04703a89685dbbe35501c2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 06:45:53 +0000
+Subject: rust_binder: introduce TransactionInfo
+
+From: Alice Ryhl <aliceryhl@google.com>
+
+[ Upstream commit 5326a18e3e640061ca4b65c1b732feaeace61c39 ]
+
+Rust Binder exposes information about transactions that are sent in
+various ways: printing to the kernel log, tracepoints, files in
+binderfs, and the upcoming netlink support. Currently all these
+mechanisms use disparate ways of obtaining the same information, so
+let's introduce a single Info struct that collects all the required
+information in a single place, so that all of these different mechanisms
+can operate in a more uniform way.
+
+For now, the new info struct is only used to replace a few things:
+* The BinderTransactionDataSg struct that is passed as an argument to
+  several methods is removed as the information is moved into the new
+  info struct and passed down that way.
+* The oneway spam detection fields on Transaction and Allocation can be
+  removed, as the information can be returned to the caller via the
+  mutable info struct instead.
+But several other uses of the info struct are planned in follow-up
+patches.
+
+Signed-off-by: Alice Ryhl <aliceryhl@google.com>
+Link: https://patch.msgid.link/20260306-transaction-info-v1-1-fda58fca558b@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+[aliceryhl: explicitly impl Zeroable for UserPtr instead of using derive]
+Signed-off-by: Alice Ryhl <aliceryhl@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/android/binder/allocation.rs  |   3 -
+ drivers/android/binder/error.rs       |  10 +-
+ drivers/android/binder/process.rs     |  15 +-
+ drivers/android/binder/thread.rs      | 190 +++++++++++++++-----------
+ drivers/android/binder/transaction.rs |  83 ++++++-----
+ rust/kernel/uaccess.rs                |   3 +
+ 6 files changed, 170 insertions(+), 134 deletions(-)
+
+diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs
+index a9475e136d76fe..3aaeefef6bdd99 100644
+--- a/drivers/android/binder/allocation.rs
++++ b/drivers/android/binder/allocation.rs
+@@ -56,7 +56,6 @@ pub(crate) struct Allocation {
+     pub(crate) process: Arc<Process>,
+     allocation_info: Option<AllocationInfo>,
+     free_on_drop: bool,
+-    pub(crate) oneway_spam_detected: bool,
+     #[allow(dead_code)]
+     pub(crate) debug_id: usize,
+ }
+@@ -68,7 +67,6 @@ impl Allocation {
+         offset: usize,
+         size: usize,
+         ptr: usize,
+-        oneway_spam_detected: bool,
+     ) -> Self {
+         Self {
+             process,
+@@ -76,7 +74,6 @@ impl Allocation {
+             size,
+             ptr,
+             debug_id,
+-            oneway_spam_detected,
+             allocation_info: None,
+             free_on_drop: true,
+         }
+diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs
+index 9921827267d0d6..c6a834071f8cef 100644
+--- a/drivers/android/binder/error.rs
++++ b/drivers/android/binder/error.rs
+@@ -12,7 +12,7 @@ pub(crate) type BinderResult<T = ()> = core::result::Result<T, BinderError>;
+ /// errno.
+ pub(crate) struct BinderError {
+     pub(crate) reply: u32,
+-    source: Option<Error>,
++    pub(crate) source: Option<Error>,
+ }
+ impl BinderError {
+@@ -40,14 +40,6 @@ impl BinderError {
+     pub(crate) fn is_dead(&self) -> bool {
+         self.reply == BR_DEAD_REPLY
+     }
+-
+-    pub(crate) fn as_errno(&self) -> kernel::ffi::c_int {
+-        self.source.unwrap_or(EINVAL).to_errno()
+-    }
+-
+-    pub(crate) fn should_pr_warn(&self) -> bool {
+-        self.source.is_some()
+-    }
+ }
+ /// Convert an errno into a `BinderError` and store the errno used to construct it. The errno
+diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
+index 8ccc9ffbcd90e3..d33af8a043676c 100644
+--- a/drivers/android/binder/process.rs
++++ b/drivers/android/binder/process.rs
+@@ -47,6 +47,7 @@ use crate::{
+     range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs},
+     stats::BinderStats,
+     thread::{PushWorkRes, Thread},
++    transaction::TransactionInfo,
+     BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead,
+ };
+@@ -981,16 +982,15 @@ impl Process {
+         self: &Arc<Self>,
+         debug_id: usize,
+         size: usize,
+-        is_oneway: bool,
+-        from_pid: i32,
++        info: &mut TransactionInfo,
+     ) -> BinderResult<NewAllocation> {
+         use kernel::page::PAGE_SIZE;
+         let mut reserve_new_args = ReserveNewArgs {
+             debug_id,
+             size,
+-            is_oneway,
+-            pid: from_pid,
++            is_oneway: info.is_oneway(),
++            pid: info.from_pid,
+             ..ReserveNewArgs::default()
+         };
+@@ -1006,13 +1006,13 @@ impl Process {
+             reserve_new_args = alloc_request.make_alloc()?;
+         };
++        info.oneway_spam_suspect = new_alloc.oneway_spam_detected;
+         let res = Allocation::new(
+             self.clone(),
+             debug_id,
+             new_alloc.offset,
+             size,
+             addr + new_alloc.offset,
+-            new_alloc.oneway_spam_detected,
+         );
+         // This allocation will be marked as in use until the `Allocation` is used to free it.
+@@ -1044,7 +1044,7 @@ impl Process {
+         let mapping = inner.mapping.as_mut()?;
+         let offset = ptr.checked_sub(mapping.address)?;
+         let (size, debug_id, odata) = mapping.alloc.reserve_existing(offset).ok()?;
+-        let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr, false);
++        let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr);
+         if let Some(data) = odata {
+             alloc.set_info(data);
+         }
+@@ -1397,8 +1397,7 @@ impl Process {
+                 .alloc
+                 .take_for_each(|offset, size, debug_id, odata| {
+                     let ptr = offset + address;
+-                    let mut alloc =
+-                        Allocation::new(self.clone(), debug_id, offset, size, ptr, false);
++                    let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr);
+                     if let Some(data) = odata {
+                         alloc.set_info(data);
+                     }
+diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
+index 6a2ddf0039c145..93aa98c2a173a0 100644
+--- a/drivers/android/binder/thread.rs
++++ b/drivers/android/binder/thread.rs
+@@ -19,7 +19,7 @@ use kernel::{
+     sync::{Arc, SpinLock},
+     task::Task,
+     types::ARef,
+-    uaccess::UserSlice,
++    uaccess::{UserPtr, UserSlice, UserSliceReader},
+     uapi,
+ };
+@@ -30,7 +30,7 @@ use crate::{
+     process::{GetWorkOrRegister, Process},
+     ptr_align,
+     stats::GLOBAL_STATS,
+-    transaction::Transaction,
++    transaction::{Transaction, TransactionInfo},
+     BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,
+ };
+@@ -948,13 +948,11 @@ impl Thread {
+     pub(crate) fn copy_transaction_data(
+         &self,
+         to_process: Arc<Process>,
+-        tr: &BinderTransactionDataSg,
++        info: &mut TransactionInfo,
+         debug_id: usize,
+         allow_fds: bool,
+         txn_security_ctx_offset: Option<&mut usize>,
+     ) -> BinderResult<NewAllocation> {
+-        let trd = &tr.transaction_data;
+-        let is_oneway = trd.flags & TF_ONE_WAY != 0;
+         let mut secctx = if let Some(offset) = txn_security_ctx_offset {
+             let secid = self.process.cred.get_secid();
+             let ctx = match security::SecurityCtx::from_secid(secid) {
+@@ -969,10 +967,10 @@ impl Thread {
+             None
+         };
+-        let data_size = trd.data_size.try_into().map_err(|_| EINVAL)?;
++        let data_size = info.data_size;
+         let aligned_data_size = ptr_align(data_size).ok_or(EINVAL)?;
+-        let offsets_size: usize = trd.offsets_size.try_into().map_err(|_| EINVAL)?;
+-        let buffers_size: usize = tr.buffers_size.try_into().map_err(|_| EINVAL)?;
++        let offsets_size = info.offsets_size;
++        let buffers_size = info.buffers_size;
+         let aligned_secctx_size = match secctx.as_ref() {
+             Some((_offset, ctx)) => ptr_align(ctx.len()).ok_or(EINVAL)?,
+             None => 0,
+@@ -995,32 +993,25 @@ impl Thread {
+             size_of::<u64>(),
+         );
+         let secctx_off = aligned_data_size + offsets_size + buffers_size;
+-        let mut alloc =
+-            match to_process.buffer_alloc(debug_id, len, is_oneway, self.process.task.pid()) {
+-                Ok(alloc) => alloc,
+-                Err(err) => {
+-                    pr_warn!(
+-                        "Failed to allocate buffer. len:{}, is_oneway:{}",
+-                        len,
+-                        is_oneway
+-                    );
+-                    return Err(err);
+-                }
+-            };
++        let mut alloc = match to_process.buffer_alloc(debug_id, len, info) {
++            Ok(alloc) => alloc,
++            Err(err) => {
++                pr_warn!(
++                    "Failed to allocate buffer. len:{}, is_oneway:{}",
++                    len,
++                    info.is_oneway(),
++                );
++                return Err(err);
++            }
++        };
+-        // SAFETY: This accesses a union field, but it's okay because the field's type is valid for
+-        // all bit-patterns.
+-        let trd_data_ptr = unsafe { &trd.data.ptr };
+-        let mut buffer_reader =
+-            UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), data_size).reader();
++        let mut buffer_reader = UserSlice::new(info.data_ptr, data_size).reader();
+         let mut end_of_previous_object = 0;
+         let mut sg_state = None;
+         // Copy offsets if there are any.
+         if offsets_size > 0 {
+-            let mut offsets_reader =
+-                UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as _), offsets_size)
+-                    .reader();
++            let mut offsets_reader = UserSlice::new(info.offsets_ptr, offsets_size).reader();
+             let offsets_start = aligned_data_size;
+             let offsets_end = aligned_data_size + offsets_size;
+@@ -1194,37 +1185,92 @@ impl Thread {
+         }
+     }
+-    fn transaction<T>(self: &Arc<Self>, tr: &BinderTransactionDataSg, inner: T)
+-    where
+-        T: FnOnce(&Arc<Self>, &BinderTransactionDataSg) -> BinderResult,
+-    {
+-        if let Err(err) = inner(self, tr) {
+-            if err.should_pr_warn() {
+-                let mut ee = self.inner.lock().extended_error;
+-                ee.command = err.reply;
+-                ee.param = err.as_errno();
+-                pr_warn!(
+-                    "Transaction failed: {:?} my_pid:{}",
+-                    err,
+-                    self.process.pid_in_current_ns()
+-                );
++    // No inlining avoids allocating stack space for `BinderTransactionData` for the entire
++    // duration of `transaction()`.
++    #[inline(never)]
++    fn read_transaction_info(
++        &self,
++        cmd: u32,
++        reader: &mut UserSliceReader,
++        info: &mut TransactionInfo,
++    ) -> Result<()> {
++        let td = match cmd {
++            BC_TRANSACTION | BC_REPLY => {
++                reader.read::<BinderTransactionData>()?.with_buffers_size(0)
++            }
++            BC_TRANSACTION_SG | BC_REPLY_SG => reader.read::<BinderTransactionDataSg>()?,
++            _ => return Err(EINVAL),
++        };
++
++        // SAFETY: Above `read` call initializes all bytes, so this union read is ok.
++        let trd_data_ptr = unsafe { &td.transaction_data.data.ptr };
++
++        info.is_reply = matches!(cmd, BC_REPLY | BC_REPLY_SG);
++        info.from_pid = self.process.task.pid();
++        info.from_tid = self.id;
++        info.code = td.transaction_data.code;
++        info.flags = td.transaction_data.flags;
++        info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize);
++        info.data_size = td.transaction_data.data_size as usize;
++        info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize);
++        info.offsets_size = td.transaction_data.offsets_size as usize;
++        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 };
++        Ok(())
++    }
++
++    #[inline(never)]
++    fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Result<()> {
++        let mut info = TransactionInfo::zeroed();
++        self.read_transaction_info(cmd, reader, &mut info)?;
++
++        let ret = if info.is_reply {
++            self.reply_inner(&mut info)
++        } else if info.is_oneway() {
++            self.oneway_transaction_inner(&mut info)
++        } else {
++            self.transaction_inner(&mut info)
++        };
++
++        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();
++                info.reply = err.reply;
++
++                {
++                    let mut ee = self.inner.lock().extended_error;
++                    ee.command = err.reply;
++                    ee.param = source.to_errno();
++                }
++
++                pr_warn!(
++                    "{}:{} transaction to {} failed: {source:?}",
++                    info.from_pid,
++                    info.from_tid,
++                    info.to_pid
++                );
++            }
+         }
++
++        Ok(())
+     }
+-    fn transaction_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
+-        // SAFETY: Handle's type has no invalid bit patterns.
+-        let handle = unsafe { tr.transaction_data.target.handle };
+-        let node_ref = self.process.get_transaction_node(handle)?;
++    fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
++        let node_ref = self.process.get_transaction_node(info.target_handle)?;
++        info.to_pid = node_ref.node.owner.task.pid();
+         security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?;
+         // TODO: We need to ensure that there isn't a pending transaction in the work queue. How
+         // could this happen?
+         let top = self.top_of_transaction_stack()?;
+         let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
+         let completion = list_completion.clone_arc();
+-        let transaction = Transaction::new(node_ref, top, self, tr)?;
++        let transaction = Transaction::new(node_ref, top, self, info)?;
+         // Check that the transaction stack hasn't changed while the lock was released, then update
+         // it with the new transaction.
+@@ -1240,7 +1286,7 @@ impl Thread {
+             inner.push_work_deferred(list_completion);
+         }
+-        if let Err(e) = transaction.submit() {
++        if let Err(e) = transaction.submit(info) {
+             completion.skip();
+             // Define `transaction` first to drop it after `inner`.
+             let transaction;
+@@ -1253,18 +1299,21 @@ impl Thread {
+         }
+     }
+-    fn reply_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
++    fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
+         let orig = self.inner.lock().pop_transaction_to_reply(self)?;
+         if !orig.from.is_current_transaction(&orig) {
+             return Err(EINVAL.into());
+         }
++        info.to_tid = orig.from.id;
++        info.to_pid = orig.from.process.task.pid();
++
+         // We need to complete the transaction even if we cannot complete building the reply.
+         let out = (|| -> BinderResult<_> {
+             let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
+             let process = orig.from.process.clone();
+             let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
+-            let reply = Transaction::new_reply(self, process, tr, allow_fds)?;
++            let reply = Transaction::new_reply(self, process, info, allow_fds)?;
+             self.inner.lock().push_work(completion);
+             orig.from.deliver_reply(Ok(reply), &orig);
+             Ok(())
+@@ -1285,16 +1334,12 @@ impl Thread {
+         out
+     }
+-    fn oneway_transaction_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
+-        // SAFETY: The `handle` field is valid for all possible byte values, so reading from the
+-        // union is okay.
+-        let handle = unsafe { tr.transaction_data.target.handle };
+-        let node_ref = self.process.get_transaction_node(handle)?;
++    fn oneway_transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
++        let node_ref = self.process.get_transaction_node(info.target_handle)?;
++        info.to_pid = node_ref.node.owner.task.pid();
+         security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?;
+-        let transaction = Transaction::new(node_ref, None, self, tr)?;
+-        let code = if self.process.is_oneway_spam_detection_enabled()
+-            && transaction.oneway_spam_detected
+-        {
++        let transaction = Transaction::new(node_ref, None, self, info)?;
++        let code = if self.process.is_oneway_spam_detection_enabled() && info.oneway_spam_suspect {
+             BR_ONEWAY_SPAM_SUSPECT
+         } else {
+             BR_TRANSACTION_COMPLETE
+@@ -1302,7 +1347,7 @@ impl Thread {
+         let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?;
+         let completion = list_completion.clone_arc();
+         self.inner.lock().push_work(list_completion);
+-        match transaction.submit() {
++        match transaction.submit(info) {
+             Ok(()) => Ok(()),
+             Err(err) => {
+                 completion.skip();
+@@ -1323,29 +1368,8 @@ impl Thread {
+             GLOBAL_STATS.inc_bc(cmd);
+             self.process.stats.inc_bc(cmd);
+             match cmd {
+-                BC_TRANSACTION => {
+-                    let tr = reader.read::<BinderTransactionData>()?.with_buffers_size(0);
+-                    if tr.transaction_data.flags & TF_ONE_WAY != 0 {
+-                        self.transaction(&tr, Self::oneway_transaction_inner);
+-                    } else {
+-                        self.transaction(&tr, Self::transaction_inner);
+-                    }
+-                }
+-                BC_TRANSACTION_SG => {
+-                    let tr = reader.read::<BinderTransactionDataSg>()?;
+-                    if tr.transaction_data.flags & TF_ONE_WAY != 0 {
+-                        self.transaction(&tr, Self::oneway_transaction_inner);
+-                    } else {
+-                        self.transaction(&tr, Self::transaction_inner);
+-                    }
+-                }
+-                BC_REPLY => {
+-                    let tr = reader.read::<BinderTransactionData>()?.with_buffers_size(0);
+-                    self.transaction(&tr, Self::reply_inner)
+-                }
+-                BC_REPLY_SG => {
+-                    let tr = reader.read::<BinderTransactionDataSg>()?;
+-                    self.transaction(&tr, Self::reply_inner)
++                BC_TRANSACTION | BC_TRANSACTION_SG | BC_REPLY | BC_REPLY_SG => {
++                    self.transaction(cmd, &mut reader)?;
+                 }
+                 BC_FREE_BUFFER => {
+                     let buffer = self.process.buffer_get(reader.read()?);
+diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
+index 50bd1cee0f1a36..733f452d8f49dd 100644
+--- a/drivers/android/binder/transaction.rs
++++ b/drivers/android/binder/transaction.rs
+@@ -8,7 +8,7 @@ use kernel::{
+     seq_file::SeqFile,
+     seq_print,
+     sync::{Arc, SpinLock},
+-    task::Kuid,
++    task::{Kuid, Pid},
+     time::{Instant, Monotonic},
+     types::ScopeGuard,
+ };
+@@ -24,6 +24,33 @@ use crate::{
+     BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
+ };
++#[derive(Zeroable)]
++pub(crate) struct TransactionInfo {
++    pub(crate) from_pid: Pid,
++    pub(crate) from_tid: Pid,
++    pub(crate) to_pid: Pid,
++    pub(crate) to_tid: Pid,
++    pub(crate) code: u32,
++    pub(crate) flags: u32,
++    pub(crate) data_ptr: UserPtr,
++    pub(crate) data_size: usize,
++    pub(crate) offsets_ptr: UserPtr,
++    pub(crate) offsets_size: usize,
++    pub(crate) buffers_size: usize,
++    pub(crate) target_handle: u32,
++    pub(crate) errno: i32,
++    pub(crate) reply: u32,
++    pub(crate) oneway_spam_suspect: bool,
++    pub(crate) is_reply: bool,
++}
++
++impl TransactionInfo {
++    #[inline]
++    pub(crate) fn is_oneway(&self) -> bool {
++        self.flags & TF_ONE_WAY != 0
++    }
++}
++
+ #[pin_data(PinnedDrop)]
+ pub(crate) struct Transaction {
+     pub(crate) debug_id: usize,
+@@ -41,7 +68,6 @@ pub(crate) struct Transaction {
+     data_address: usize,
+     sender_euid: Kuid,
+     txn_security_ctx_off: Option<usize>,
+-    pub(crate) oneway_spam_detected: bool,
+     start_time: Instant<Monotonic>,
+ }
+@@ -54,17 +80,16 @@ impl Transaction {
+         node_ref: NodeRef,
+         from_parent: Option<DArc<Transaction>>,
+         from: &Arc<Thread>,
+-        tr: &BinderTransactionDataSg,
++        info: &mut TransactionInfo,
+     ) -> BinderResult<DLArc<Self>> {
+         let debug_id = super::next_debug_id();
+-        let trd = &tr.transaction_data;
+         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 };
+         let to = node_ref.node.owner.clone();
+         let mut alloc = match from.copy_transaction_data(
+             to.clone(),
+-            tr,
++            info,
+             debug_id,
+             allow_fds,
+             txn_security_ctx_off.as_mut(),
+@@ -77,15 +102,14 @@ impl Transaction {
+                 return Err(err);
+             }
+         };
+-        let oneway_spam_detected = alloc.oneway_spam_detected;
+-        if trd.flags & TF_ONE_WAY != 0 {
++        if info.is_oneway() {
+             if from_parent.is_some() {
+                 pr_warn!("Oneway transaction should not be in a transaction stack.");
+                 return Err(EINVAL.into());
+             }
+             alloc.set_info_oneway_node(node_ref.node.clone());
+         }
+-        if trd.flags & TF_CLEAR_BUF != 0 {
++        if info.flags & TF_CLEAR_BUF != 0 {
+             alloc.set_info_clear_on_drop();
+         }
+         let target_node = node_ref.node.clone();
+@@ -99,15 +123,14 @@ impl Transaction {
+             sender_euid: from.process.task.euid(),
+             from: from.clone(),
+             to,
+-            code: trd.code,
+-            flags: trd.flags,
+-            data_size: trd.data_size as _,
+-            offsets_size: trd.offsets_size as _,
++            code: info.code,
++            flags: info.flags,
++            data_size: info.data_size,
++            offsets_size: info.offsets_size,
+             data_address,
+             allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"),
+             is_outstanding: AtomicBool::new(false),
+             txn_security_ctx_off,
+-            oneway_spam_detected,
+             start_time: Instant::now(),
+         }))?)
+     }
+@@ -115,21 +138,19 @@ impl Transaction {
+     pub(crate) fn new_reply(
+         from: &Arc<Thread>,
+         to: Arc<Process>,
+-        tr: &BinderTransactionDataSg,
++        info: &mut TransactionInfo,
+         allow_fds: bool,
+     ) -> BinderResult<DLArc<Self>> {
+         let debug_id = super::next_debug_id();
+-        let trd = &tr.transaction_data;
+-        let mut alloc = match from.copy_transaction_data(to.clone(), tr, debug_id, allow_fds, None)
+-        {
+-            Ok(alloc) => alloc,
+-            Err(err) => {
+-                pr_warn!("Failure in copy_transaction_data: {:?}", err);
+-                return Err(err);
+-            }
+-        };
+-        let oneway_spam_detected = alloc.oneway_spam_detected;
+-        if trd.flags & TF_CLEAR_BUF != 0 {
++        let mut alloc =
++            match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) {
++                Ok(alloc) => alloc,
++                Err(err) => {
++                    pr_warn!("Failure in copy_transaction_data: {:?}", err);
++                    return Err(err);
++                }
++            };
++        if info.flags & TF_CLEAR_BUF != 0 {
+             alloc.set_info_clear_on_drop();
+         }
+         Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
+@@ -139,15 +160,14 @@ impl Transaction {
+             sender_euid: from.process.task.euid(),
+             from: from.clone(),
+             to,
+-            code: trd.code,
+-            flags: trd.flags,
+-            data_size: trd.data_size as _,
+-            offsets_size: trd.offsets_size as _,
++            code: info.code,
++            flags: info.flags,
++            data_size: info.data_size,
++            offsets_size: info.offsets_size,
+             data_address: alloc.ptr,
+             allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"),
+             is_outstanding: AtomicBool::new(false),
+             txn_security_ctx_off: None,
+-            oneway_spam_detected,
+             start_time: Instant::now(),
+         }))?)
+     }
+@@ -237,7 +257,7 @@ impl Transaction {
+     /// stack, otherwise uses the destination process.
+     ///
+     /// Not used for replies.
+-    pub(crate) fn submit(self: DLArc<Self>) -> BinderResult {
++    pub(crate) fn submit(self: DLArc<Self>, info: &mut TransactionInfo) -> BinderResult {
+         // Defined before `process_inner` so that the destructor runs after releasing the lock.
+         let _t_outdated;
+         let _oneway_node;
+@@ -295,6 +315,7 @@ impl Transaction {
+         }
+         let res = if let Some(thread) = self.find_target_thread() {
++            info.to_tid = thread.id;
+             match thread.push_work(self) {
+                 PushWorkRes::Ok => Ok(()),
+                 PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
+diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs
+index a8fb4764185a1a..2035c2877a2b2f 100644
+--- a/rust/kernel/uaccess.rs
++++ b/rust/kernel/uaccess.rs
+@@ -21,6 +21,9 @@ use core::mem::{size_of, MaybeUninit};
+ #[derive(Copy, Clone)]
+ pub struct UserPtr(*mut c_void);
++// SAFETY: Null pointer is a valid value.
++unsafe impl Zeroable for UserPtr {}
++
+ impl UserPtr {
+     /// Create a `UserPtr` from an integer representing the userspace address.
+     #[inline]
+-- 
+2.53.0
+
index f0e063eeed43a7a729a3ad021f44b985fc9c4839..c181a2dbe7e56930a49b4ec4c4f65dd92857c3fb 100644 (file)
@@ -317,3 +317,13 @@ usb-typec-ucsi-pass-full-dp-config-payload-in-set_new_cam-for-dp-alt-mode.patch
 usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch
 usb-typec-ucsi-cancel-pending-work-on-system-suspend.patch
 usb-gadget-f_fs-fix-dma-fence-leak.patch
+mm-shmem-fix-potential-livelock-issue-for-shmem-dire.patch
+x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch
+rust_binder-introduce-transactioninfo.patch
+rust_binder-fix-binder_get_extended_error.patch
+bpf-support-for-hardening-against-jit-spraying.patch
+x86-bugs-enable-ibpb-flush-on-bpf-jit-allocation.patch
+bpf-restrict-jit-predictor-flush-to-cbpf.patch
+bpf-skip-redundant-ibpb-in-pack-allocator.patch
+bpf-prefer-packs-that-won-t-trigger-an-ibpb-flush-on.patch
+bpf-prefer-dirty-packs-for-ebpf-allocations.patch
diff --git a/queue-6.18/x86-bugs-enable-ibpb-flush-on-bpf-jit-allocation.patch b/queue-6.18/x86-bugs-enable-ibpb-flush-on-bpf-jit-allocation.patch
new file mode 100644 (file)
index 0000000..ba82801
--- /dev/null
@@ -0,0 +1,143 @@
+From f5f1d5c4b0ec36c19d26e795af46a3d986b3a8e7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:11:52 -0700
+Subject: x86/bugs: Enable IBPB flush on BPF JIT allocation
+
+From: Pawan Gupta <pawan.kumar.gupta@linux.intel.com>
+
+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 <pawan.kumar.gupta@linux.intel.com>
+Acked-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Dave Hansen <dave.hansen@linux.intel.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 08ed5a2e46a5fd..71e9861a16a896 100644
+--- a/arch/x86/include/asm/nospec-branch.h
++++ b/arch/x86/include/asm/nospec-branch.h
+@@ -386,6 +386,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 d7fa03bf51b451..fd0b7880cf7e5a 100644
+--- a/arch/x86/kernel/cpu/bugs.c
++++ b/arch/x86/kernel/cpu/bugs.c
+@@ -16,6 +16,7 @@
+ #include <linux/sched/smt.h>
+ #include <linux/pgtable.h>
+ #include <linux/bpf.h>
++#include <linux/filter.h>
+ #include <asm/spec-ctrl.h>
+ #include <asm/cmdline.h>
+@@ -1796,8 +1797,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"
+@@ -2240,8 +2254,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;
+@@ -2383,6 +2396,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())
+@@ -2459,6 +2493,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)
+@@ -3544,9 +3586,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";
+-- 
+2.53.0
+
diff --git a/queue-6.18/x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch b/queue-6.18/x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch
new file mode 100644 (file)
index 0000000..56bbb5c
--- /dev/null
@@ -0,0 +1,134 @@
+From e004cf0dca6984afefb36be7b2ac3aa4319926de Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 15:17:37 -0700
+Subject: x86,fs/resctrl: Prevent out-of-bounds access while offlining CPU when
+ SNC enabled
+
+From: Reinette Chatre <reinette.chatre@intel.com>
+
+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 <sashiko-bot@kernel.org>
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
+Cc: <stable@kernel.org>
+Link: https://patch.msgid.link/16137433df42f85013b2f7a53626795cbd6637b9.1781029125.git.reinette.chatre@intel.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/cpu/resctrl/monitor.c |  8 +++++-
+ fs/resctrl/monitor.c                  | 39 +++++++++++++++------------
+ 2 files changed, 29 insertions(+), 18 deletions(-)
+
+diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c
+index fe1a2aa53c16ab..4b2b539ddff953 100644
+--- a/arch/x86/kernel/cpu/resctrl/monitor.c
++++ b/arch/x86/kernel/cpu/resctrl/monitor.c
+@@ -243,14 +243,20 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d,
+                          u64 *val, void *ignored)
+ {
+       struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d);
+-      int cpu = cpumask_any(&d->hdr.cpu_mask);
+       struct arch_mbm_state *am;
+       u64 msr_val;
+       u32 prmid;
++      int cpu;
+       int ret;
+       resctrl_arch_rmid_read_context_check();
++      if (cpumask_empty(&d->hdr.cpu_mask)) {
++              pr_warn_once("Domain %d has no CPUs\n", d->hdr.id);
++              return -EINVAL;
++      }
++
++      cpu = cpumask_any(&d->hdr.cpu_mask);
+       prmid = logical_rmid_to_physical_rmid(cpu, rmid);
+       ret = __rmid_read_phys(prmid, eventid, &msr_val);
+diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c
+index 572a9925bd6ca0..7012a75814ac36 100644
+--- a/fs/resctrl/monitor.c
++++ b/fs/resctrl/monitor.c
+@@ -135,9 +135,9 @@ void __check_limbo(struct rdt_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;
+-      bool rmid_dirty;
+       u64 val = 0;
+       arch_mon_ctx = resctrl_arch_mon_ctx_alloc(r, QOS_L3_OCCUP_EVENT_ID);
+@@ -159,22 +159,27 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free)
+                       break;
+               entry = __rmid_entry(idx);
+-              if (resctrl_arch_rmid_read(r, d, entry->closid, entry->rmid,
+-                                         QOS_L3_OCCUP_EVENT_ID, &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, entry->closid,
++                                                 entry->rmid, QOS_L3_OCCUP_EVENT_ID,
++                                                 &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) {
+-- 
+2.53.0
+
diff --git a/queue-6.6/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch b/queue-6.6/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch
new file mode 100644 (file)
index 0000000..c02cab5
--- /dev/null
@@ -0,0 +1,49 @@
+From d37543be961a274a8327328af56fb3f85cae2f2e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 18:45:26 +0300
+Subject: iio: temperature: ltc2983: Fix n_wires default bypassing rotation
+ check
+
+From: Liviu Stan <liviu.stan@analog.com>
+
+[ Upstream commit 434c150752675f44dc52c384a7fa22e5176bc35a ]
+
+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 <liviu.stan@analog.com>
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Liviu Stan <liviu.stan@analog.com>
+
+[ Liviu Stan: resolve conflict - keep fwnode_handle *ref declaration
+  which was removed upstream ]
+
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 08485c4ecd52e5..a2e4ddf095d88a 100644
+--- a/drivers/iio/temperature/ltc2983.c
++++ b/drivers/iio/temperature/ltc2983.c
+@@ -751,7 +751,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
+       int ret = 0;
+       struct device *dev = &st->spi->dev;
+       struct fwnode_handle *ref;
+-      u32 excitation_current = 0, n_wires = 0;
++      u32 excitation_current = 0, n_wires = 2;
+       rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
+       if (!rtd)
+-- 
+2.53.0
+
diff --git a/queue-6.6/pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch b/queue-6.6/pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch
new file mode 100644 (file)
index 0000000..5baf6bb
--- /dev/null
@@ -0,0 +1,134 @@
+From e4d4bd7886101ffcae1eb1febf06fd435fe37442 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 23:27:26 +0100
+Subject: PCI: Always lift 2.5GT/s restriction in PCIe failed link retraining
+
+From: Maciej W. Rozycki <macro@orcam.me.uk>
+
+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 <macro@orcam.me.uk>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alok Tiwari <alok.a.tiwari@oracle.com>
+Cc: stable@vger.kernel.org # v6.5+
+Link: https://patch.msgid.link/alpine.DEB.2.21.2512080331530.49654@angie.orcam.me.uk
+[ Update for missing PCIe link speed helpers for 6.6.y. ]
+Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/pci/quirks.c | 51 +++++++++++++++++---------------------------
+ 1 file changed, 20 insertions(+), 31 deletions(-)
+
+diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
+index 5df3a6ea660187..28fa0a9b18c824 100644
+--- a/drivers/pci/quirks.c
++++ b/drivers/pci/quirks.c
+@@ -68,11 +68,10 @@
+  * 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
+@@ -83,12 +82,9 @@
+  */
+ 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)
+@@ -96,10 +92,9 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
+       pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2);
+       pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
++      oldlnkctl2 = lnkctl2;
+       if ((lnksta & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_DLLLA)) ==
+           PCI_EXP_LNKSTA_LBMS) {
+-              u16 oldlnkctl2 = lnkctl2;
+-
+               pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n");
+               lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
+@@ -107,35 +102,29 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
+               pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2);
+               ret = pcie_retrain_link(dev, false);
+-              if (ret) {
+-                      pci_info(dev, "retraining failed\n");
+-                      pcie_capability_write_word(dev, PCI_EXP_LNKCTL2,
+-                                                 oldlnkctl2);
+-                      pcie_retrain_link(dev, true);
+-                      return ret;
+-              }
+-
+-              pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
++              if (ret)
++                      goto err;
+       }
+-      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);
+               lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
+               lnkctl2 |= lnkcap & PCI_EXP_LNKCAP_SLS;
+               pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2);
+               ret = pcie_retrain_link(dev, 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_capability_write_word(dev, PCI_EXP_LNKCTL2, oldlnkctl2);
++
++      pcie_retrain_link(dev, true);
+       return ret;
+ }
+-- 
+2.53.0
+
index 50251058a78b3ff5bb95a8eb0aecc2642cce90f0..8738a371748dc2ca64d7302119ff02d0efc842c6 100644 (file)
@@ -257,3 +257,5 @@ usb-typec-ucsi-invert-displayport-role-assignment.patch
 usb-typec-ucsi-pass-full-dp-config-payload-in-set_new_cam-for-dp-alt-mode.patch
 usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch
 usb-typec-ucsi-cancel-pending-work-on-system-suspend.patch
+iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch
+pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch
index 4d6b16288048dd291f7b11cfaebbb1074d8e0bf0..7ea84725ba13a6bbf4f3271bd30b3ef1827fb34f 100644 (file)
@@ -337,3 +337,4 @@ usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch
 usb-gadget-f_fs-fix-dma-fence-leak.patch
 usb-gadget-f_fs-initialize-epfile-in-early-to-fix-endpoint-direction-checks.patch
 usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch
+wifi-mt76-mt7921-mt7925-fix-null-dereference-in-csa-.patch
diff --git a/queue-7.1/wifi-mt76-mt7921-mt7925-fix-null-dereference-in-csa-.patch b/queue-7.1/wifi-mt76-mt7921-mt7925-fix-null-dereference-in-csa-.patch
new file mode 100644 (file)
index 0000000..1441ed2
--- /dev/null
@@ -0,0 +1,83 @@
+From 78c47051a33bcc1f92da0d4c5f5afc2ffad92830 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 4 May 2026 07:51:06 -0700
+Subject: wifi: mt76: mt7921/mt7925: fix NULL dereference in CSA beacon
+
+From: Arjan van de Ven <arjan@linux.intel.com>
+
+[ 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 <developer@hlope.org.za>
+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 <arjan@linux.intel.com>
+Cc: linux-wireless@vger.kernel.org
+Cc: linux-mediatek@lists.infradead.org
+Cc: Felix Fietkau <nbd@nbd.name>
+Cc: Lorenzo Bianconi <lorenzo@kernel.org>
+Cc: Ryder Lee <ryder.lee@mediatek.com>
+Link: https://patch.msgid.link/20260504145107.1329197-1-arjan@linux.intel.com
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ 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 3d74fabe74085e..a326f4c95c7c86 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 79cc30a3b29efc..9dc5ee51eb9f96 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,
+-- 
+2.53.0
+