From: Sasha Levin Date: Wed, 15 Jul 2026 00:11:25 +0000 (-0400) Subject: Fixes for all trees X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b82916cc6cb49da6207226c09b1adb602c528a5b;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for all trees Signed-off-by: Sasha Levin --- 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 index 0000000000..07aa1afef3 --- /dev/null +++ b/queue-5.10/iio-adc-spear-initialize-completion-before-requestin.patch @@ -0,0 +1,79 @@ +From bfdf815f1f6afd08fb56ac84af61e6389c0b455a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 11:58:33 -0500 +Subject: iio: adc: spear: Initialize completion before requesting IRQ + +From: Maxwell Doose + +[ 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 +Reported-by: Kyungwook Boo +Reported-by: Jaeyoung Chung +Closes: https://lore.kernel.org/linux-iio/20260610115700.774689-1-jjy600901@snu.ac.kr/ +Signed-off-by: Maxwell Doose +Reviewed-by: Vladimir Zapolskiy +Cc: +Signed-off-by: Jonathan Cameron +Signed-off-by: Maxwell Doose +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..57f5201627 --- /dev/null +++ b/queue-5.10/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch @@ -0,0 +1,45 @@ +From d030a71ea48cf8fee36476ebf9d736e14688c58f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:16:13 +0300 +Subject: iio: temperature: ltc2983: Fix n_wires default bypassing rotation + check + +From: Liviu Stan + +[ 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 +Cc: +Signed-off-by: Jonathan Cameron +Signed-off-by: Liviu Stan +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-5.10/series b/queue-5.10/series index 95ef10120a..078c4243e9 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -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 index 0000000000..727eb90fb6 --- /dev/null +++ b/queue-5.15/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch @@ -0,0 +1,45 @@ +From b27b20bfecbb8408550e81b8d0101460a0bf1fce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:26:10 +0300 +Subject: iio: temperature: ltc2983: Fix n_wires default bypassing rotation + check + +From: Liviu Stan + +[ 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 +Cc: +Signed-off-by: Jonathan Cameron +Signed-off-by: Liviu Stan +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-5.15/series b/queue-5.15/series index 80abd42a4e..31b0edd6fe 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -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 index 0000000000..ca45dfd65a --- /dev/null +++ b/queue-6.1/efivarfs-expose-used-and-total-size.patch @@ -0,0 +1,189 @@ +From 37936041684be30c92a2527b891c949bee970f0d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 23:33:29 -0500 +Subject: efivarfs: expose used and total size + +From: Anisse Astier + +[ 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 +[ardb: - rename efi_reserved_space() to efivar_reserved_space() + - whitespace/coding style tweaks] +Signed-off-by: Ard Biesheuvel +Adjusted for headers in linux-6.1.y +Signed-off-by: Mario Limonciello +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include + + #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 index 0000000000..525b0309c1 --- /dev/null +++ b/queue-6.1/efivarfs-fix-statfs-on-efivarfs.patch @@ -0,0 +1,57 @@ +From 2da59d960269acedf14bd7729a5ac4e558624517 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 10 Sep 2023 06:54:45 +0200 +Subject: efivarfs: fix statfs() on efivarfs + +From: Heinrich Schuchardt + +[ 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 +[ardb: log warning on QueryVariableInfo() failure] +Reviewed-by: Ilias Apalodimas +Signed-off-by: Ard Biesheuvel +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..582710e40d --- /dev/null +++ b/queue-6.1/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch @@ -0,0 +1,49 @@ +From f944be57e55f19a7e4b7329f2f9c9ddef780128a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:34:41 +0300 +Subject: iio: temperature: ltc2983: Fix n_wires default bypassing rotation + check + +From: Liviu Stan + +[ 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 +Cc: +Signed-off-by: Jonathan Cameron +Signed-off-by: Liviu Stan + +[ 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 +--- + 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 + diff --git a/queue-6.1/series b/queue-6.1/series index 8abc47d8a8..ad8eece940 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -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 index 0000000000..ee337ab5f4 --- /dev/null +++ b/queue-6.12/pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch @@ -0,0 +1,134 @@ +From 67f5da61ba39b5a5dac82a49d4623ac759a7a2f1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +commit 72780f7964684939d7d2f69c348876213b184484 upstream. + +Discard Vendor:Device ID matching in the PCIe failed link retraining quirk +and ignore the link status for the removal of the 2.5GT/s speed clamp, +whether applied by the quirk itself or the firmware earlier on. Revert to +the original target link speed if this final link retraining has failed. + +This is so that link training noise in hot-plug scenarios does not make a +link remain clamped to the 2.5GT/s speed where an event race has led the +quirk to apply the speed clamp for one device, only to leave it in place +for a subsequent device to be plugged in. + +Refer to the Link Capabilities register directly for the maximum link speed +determination so as to streamline backporting. + +Fixes: a89c82249c37 ("PCI: Work around PCIe link training failures") +Signed-off-by: Maciej W. Rozycki +Signed-off-by: Bjorn Helgaas +Tested-by: Alok Tiwari +Cc: stable@vger.kernel.org # v6.5+ +Link: https://patch.msgid.link/alpine.DEB.2.21.2512080331530.49654@angie.orcam.me.uk +[ Update for missing PCIe link speed helpers for 6.12.y. ] +Signed-off-by: Maciej W. Rozycki +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.12/series b/queue-6.12/series index c9dc06c2ae..5fad36f31b 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -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 index 0000000000..f53a021a64 --- /dev/null +++ b/queue-6.12/x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch @@ -0,0 +1,129 @@ +From 6d17092213afa16c6188165aa5619c2a69e9f530 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +commit fc16126cc11d9f507130bf84ab137ee0938c900e upstream. + +The architecture updates the cpu_mask in a domain's header to track which +online CPUs are associated with the domain. When this mask becomes empty +the architecture initiates offline of the domain that includes calling +on resctrl fs to offline the domain. If it is a monitoring domain in +which LLC occupancy is tracked resctrl fs forces the limbo handler to +clear all busy RMID state associated with the domain. + +The limbo handler always reads the current event value associated with a +busy RMID irrespective of it being checked as part of regular "is it still +busy" check or whether it will be forced released anyway. When reading an +RMID on a system with SNC enabled the "logical RMID" is converted to the +"physical RMID" and this conversion requires the NUMA node ID of the +resctrl monitoring domain that is in turn determined by querying the NUMA +node ID of any CPU belonging to the monitoring domain. + +When the monitoring domain is going offline its cpu_mask is empty causing +the NUMA node ID query via cpu_to_node() to be done with "nr_cpu_ids" as +argument resulting in an out-of-bounds access. + +Refactor the limbo handler to skip reading the RMID when the RMID will +just be forced to no longer be dirty in the domain anyway. Add a safety +check to the architecture's RMID reader to protect against this scenario. + +Fixes: e13db55b5a0d ("x86/resctrl: Introduce snc_nodes_per_l3_cache") +Closes: https://sashiko.dev/#/patchset/cover.1780456704.git.reinette.chatre%40intel.com?part=9 +Reported-by: Sashiko +Signed-off-by: Reinette Chatre +Signed-off-by: Borislav Petkov (AMD) +Cc: +Link: https://patch.msgid.link/16137433df42f85013b2f7a53626795cbd6637b9.1781029125.git.reinette.chatre@intel.com +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..978cb87375 --- /dev/null +++ b/queue-6.18/bpf-prefer-dirty-packs-for-ebpf-allocations.patch @@ -0,0 +1,49 @@ +From 682abc7bee5723cea5d781b6645259dbf973f82c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:12:53 -0700 +Subject: bpf: Prefer dirty packs for eBPF allocations + +From: Pawan Gupta + +commit b72e29e0f7ee329d89f86db8700c8ea99b4a370a upstream. + +The pack allocator only flushes predictors when reusing a dirty pack for +cBPF, eBPF allocations never trigger a flush. Currently, eBPF picks the +first free pack, which could be a clean pack. As an optimization, leaving +a clean pack for cBPF can avoid flushes. + +Prefer dirty packs for eBPF and keep clean packs free for cBPF. This +mirrors the existing cBPF preference for clean packs: each program kind +prefers the pack that avoids an extra flush, and falls back to the other +kind only when no preferred pack has room. eBPF reuse of a dirty pack is +harmless since eBPF being privileged does not flush. + +Signed-off-by: Pawan Gupta +Acked-by: Daniel Borkmann +Signed-off-by: Daniel Borkmann +Signed-off-by: Sasha Levin +--- + kernel/bpf/core.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c +index 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 index 0000000000..e97e2f35fd --- /dev/null +++ b/queue-6.18/bpf-prefer-packs-that-won-t-trigger-an-ibpb-flush-on.patch @@ -0,0 +1,88 @@ +From 07ecae528d2d2edf01835b1535bd227285872dfb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +commit a9b1f19a6a673ba06820898d0f1ad02883ea1639 upstream. + +Currently BPF pack allocator picks the chunks from the first available +pack. While this is okay, it naturally leads to more frequent flushes +when there are multiple packs in the system that weren't used since the +last flush. + +As an optimization prefer allocating the new programs from packs that +are unused since last flush. When all packs are dirty, allocation forces +a flush and marks all packs clean. + +Below are some future optimizations ideas: + + 1. Currently, the "dirty" tracking is only done at the pack-level. + Flush frequency can further be reduced with chunk-level tracking. + This requires a new bitmap per-pack to track the dirty state. + 2. IBPB flush is done on all CPUs, even if only a single CPU ran the + BPF program. On a system with hundreds of CPUs this could be a + major bottleneck forcing hundreds of IPIs to deliver the flush. + The solution is to track the CPUs where a BPF program ran, and + issue IBPB only on those CPUs. + 3. Avoid IBPB when flush is already done at other sources (e.g. + context switch). + +Signed-off-by: Pawan Gupta +Acked-by: Daniel Borkmann +Signed-off-by: Daniel Borkmann +Signed-off-by: Sasha Levin +--- + kernel/bpf/core.c | 27 ++++++++++++++++++++++++--- + 1 file changed, 24 insertions(+), 3 deletions(-) + +diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c +index 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 index 0000000000..e46b167e8b --- /dev/null +++ b/queue-6.18/bpf-restrict-jit-predictor-flush-to-cbpf.patch @@ -0,0 +1,236 @@ +From d73e19d7bddfdf4bc77edb2596952637f02308c1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:12:07 -0700 +Subject: bpf: Restrict JIT predictor flush to cBPF + +From: Pawan Gupta + +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 +Acked-by: Daniel Borkmann +Signed-off-by: Daniel Borkmann +Signed-off-by: Sasha Levin +--- + arch/arm64/net/bpf_jit_comp.c | 4 ++-- + arch/loongarch/net/bpf_jit.c | 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 index 0000000000..80a82990c3 --- /dev/null +++ b/queue-6.18/bpf-skip-redundant-ibpb-in-pack-allocator.patch @@ -0,0 +1,81 @@ +From 17fa250049b03cbe5bff7f5fadb7622fb3d3c790 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:12:23 -0700 +Subject: bpf: Skip redundant IBPB in pack allocator + +From: Pawan Gupta + +commit a23c1c5396a91680703360d1ee28a44657c503c4 upstream. + +bpf_prog_pack_alloc() issues IBPB on all CPUs on every cBPF allocation, +even when reusing chunks from an existing pack where no new memory was +touched since the last IBPB. + +Since IBPB on all CPUs is heavy, Dave Hansen suggested to track allocation +since last IBPB, and only issue IBPB at reuse for the chunks that have not +seen an IBPB since they were last freed. + +Track per-pack whether an IBPB is needed via arch_flush_needed. Set it when +allocating a chunk, reset on IBPB flush. On reuse, conditionally issue the +flush. Since IBPB invalidates all BTB entries, clear the flag on all packs +after flushing. + +Signed-off-by: Pawan Gupta +Acked-by: Daniel Borkmann +Signed-off-by: Daniel Borkmann +Signed-off-by: Sasha Levin +--- + kernel/bpf/core.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c +index 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 index 0000000000..6b1954cb8f --- /dev/null +++ b/queue-6.18/bpf-support-for-hardening-against-jit-spraying.patch @@ -0,0 +1,120 @@ +From f8258f4ab04d2a59f347a2412743bf79669faef9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:11:37 -0700 +Subject: bpf: Support for hardening against JIT spraying + +From: Pawan Gupta + +commit 96cce16e26dd02a8678f1e87f88a4b5cdb63b995 upstream. + +The BPF JIT allocator packs many small programs into larger executable +allocations and reuses space within those allocations as programs are +loaded and freed. When fresh code is written into space that a previous +program occupied, an indirect jump into the new program can reuse a branch +prediction left behind by the old one. + +Flush the indirect branch predictors before reusing JIT memory so that +indirect jumps into a newly written program don't reuse predictions from an +old program that occupied the same space. + +Introduce bpf_arch_pred_flush_enabled static key and bpf_arch_pred_flush +static call for flushing the branch predictors on JIT memory reuse. +Architectures that need a flush, can update it to a predictor flush +function. By default, its a NOP and does not emit any CALL. + +Allocations larger than a pack are not covered by this flush. That is safe +because cBPF programs (the unprivileged attack surface) are bounded well +below a pack size. Issue a warning if this assumption is ever violated +while the flush is active. + +Signed-off-by: Pawan Gupta +Acked-by: Daniel Borkmann +Signed-off-by: Daniel Borkmann +Signed-off-by: Sasha Levin +--- + include/linux/filter.h | 10 ++++++++++ + kernel/bpf/core.c | 19 +++++++++++++++++++ + 2 files changed, 29 insertions(+) + +diff --git a/include/linux/filter.h b/include/linux/filter.h +index cf7a0bce1bb6d8..3c4dd718beced8 100644 +--- a/include/linux/filter.h ++++ b/include/linux/filter.h +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 index 0000000000..13565ac684 --- /dev/null +++ b/queue-6.18/mm-shmem-fix-potential-livelock-issue-for-shmem-dire.patch @@ -0,0 +1,99 @@ +From 4703c3acf6204021d4625799da6b8f78c6fae7ff Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Jul 2026 10:09:22 +0800 +Subject: mm: shmem: fix potential livelock issue for shmem direct swapin + +From: Baolin Wang + +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 +Closes: https://lore.kernel.org/all/700a2cbf90a2484f979aac858f08f5d4@xiaomi.com/ +Signed-off-by: Baolin Wang +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..2251418516 --- /dev/null +++ b/queue-6.18/rust_binder-fix-binder_get_extended_error.patch @@ -0,0 +1,287 @@ +From b834619c38e13ccf912fe057c0127e3a7da0faf4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 06:45:54 +0000 +Subject: rust_binder: fix BINDER_GET_EXTENDED_ERROR + +From: Alice Ryhl + +[ 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 +Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver") +Signed-off-by: Alice Ryhl +Acked-by: Carlos Llamas +Link: https://patch.msgid.link/20260605-set-extended-error-v3-1-d60b69a75f97@google.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Alice Ryhl +Signed-off-by: Sasha Levin +--- + 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, u32>, + transaction: &DArc, ++ extended_error: Option, + ) { +- if self.deliver_single_reply(reply, transaction) { ++ if self.deliver_single_reply(reply, transaction, extended_error) { + transaction.from.unwind_transaction_stack(); + } + } +@@ -1132,6 +1143,7 @@ impl Thread { + &self, + reply: Result, u32>, + transaction: &DArc, ++ extended_error: Option, + ) -> 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, + info: &mut TransactionInfo, + ) -> BinderResult> { +- let debug_id = super::next_debug_id(); + let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0; + let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0; + let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None }; +@@ -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> { +- 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 index 0000000000..bf3337ef12 --- /dev/null +++ b/queue-6.18/rust_binder-introduce-transactioninfo.patch @@ -0,0 +1,668 @@ +From 33e07cce06e5933bfa04703a89685dbbe35501c2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 14 Jul 2026 06:45:53 +0000 +Subject: rust_binder: introduce TransactionInfo + +From: Alice Ryhl + +[ 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 +Link: https://patch.msgid.link/20260306-transaction-info-v1-1-fda58fca558b@google.com +Signed-off-by: Greg Kroah-Hartman +[aliceryhl: explicitly impl Zeroable for UserPtr instead of using derive] +Signed-off-by: Alice Ryhl +Signed-off-by: Sasha Levin +--- + 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, + allocation_info: Option, + 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 = core::result::Result; + /// errno. + pub(crate) struct BinderError { + pub(crate) reply: u32, +- source: Option, ++ pub(crate) source: Option, + } + + 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, + debug_id: usize, + size: usize, +- is_oneway: bool, +- from_pid: i32, ++ info: &mut TransactionInfo, + ) -> BinderResult { + 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, +- tr: &BinderTransactionDataSg, ++ info: &mut TransactionInfo, + debug_id: usize, + allow_fds: bool, + txn_security_ctx_offset: Option<&mut usize>, + ) -> BinderResult { +- 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::(), + ); + 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(self: &Arc, tr: &BinderTransactionDataSg, inner: T) +- where +- T: FnOnce(&Arc, &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::()?.with_buffers_size(0) ++ } ++ BC_TRANSACTION_SG | BC_REPLY_SG => reader.read::()?, ++ _ => 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, 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, 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, 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, tr: &BinderTransactionDataSg) -> BinderResult { ++ fn reply_inner(self: &Arc, 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, 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, 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::()?.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::()?; +- 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::()?.with_buffers_size(0); +- self.transaction(&tr, Self::reply_inner) +- } +- BC_REPLY_SG => { +- let tr = reader.read::()?; +- 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, +- pub(crate) oneway_spam_detected: bool, + start_time: Instant, + } + +@@ -54,17 +80,16 @@ impl Transaction { + node_ref: NodeRef, + from_parent: Option>, + from: &Arc, +- tr: &BinderTransactionDataSg, ++ info: &mut TransactionInfo, + ) -> BinderResult> { + 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, + to: Arc, +- tr: &BinderTransactionDataSg, ++ info: &mut TransactionInfo, + allow_fds: bool, + ) -> BinderResult> { + 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) -> BinderResult { ++ pub(crate) fn submit(self: DLArc, 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 + diff --git a/queue-6.18/series b/queue-6.18/series index f0e063eeed..c181a2dbe7 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -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 index 0000000000..ba82801726 --- /dev/null +++ b/queue-6.18/x86-bugs-enable-ibpb-flush-on-bpf-jit-allocation.patch @@ -0,0 +1,143 @@ +From f5f1d5c4b0ec36c19d26e795af46a3d986b3a8e7 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:11:52 -0700 +Subject: x86/bugs: Enable IBPB flush on BPF JIT allocation + +From: Pawan Gupta + +commit a3af84b0fa00ead01fcd0e28b5d773ff25990a0d upstream. + +Enable hardening against JIT spraying when Spectre-v2 mitigations are in +use. Specifically, issue an IBPB flush on BPF JIT memory reuse. Skip +enabling the IBPB flush if the BPF dispatcher is already using a retpoline +sequence. + +This hardening applies only when BPF-JIT is in use. Guard the enabling +under CONFIG_BPF_JIT so that bugs.c still builds with CONFIG_BPF_JIT=n. + +Signed-off-by: Pawan Gupta +Acked-by: Daniel Borkmann +Acked-by: Dave Hansen +Signed-off-by: Daniel Borkmann +Signed-off-by: Sasha Levin +--- + arch/x86/include/asm/nospec-branch.h | 4 +++ + arch/x86/kernel/cpu/bugs.c | 50 +++++++++++++++++++++++++--- + 2 files changed, 49 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h +index 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 + #include + #include ++#include + + #include + #include +@@ -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 index 0000000000..56bbb5cf43 --- /dev/null +++ b/queue-6.18/x86-fs-resctrl-prevent-out-of-bounds-access-while-of.patch @@ -0,0 +1,134 @@ +From e004cf0dca6984afefb36be7b2ac3aa4319926de Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +commit fc16126cc11d9f507130bf84ab137ee0938c900e upstream. + +The architecture updates the cpu_mask in a domain's header to track which +online CPUs are associated with the domain. When this mask becomes empty +the architecture initiates offline of the domain that includes calling +on resctrl fs to offline the domain. If it is a monitoring domain in +which LLC occupancy is tracked resctrl fs forces the limbo handler to +clear all busy RMID state associated with the domain. + +The limbo handler always reads the current event value associated with a +busy RMID irrespective of it being checked as part of regular "is it still +busy" check or whether it will be forced released anyway. When reading an +RMID on a system with SNC enabled the "logical RMID" is converted to the +"physical RMID" and this conversion requires the NUMA node ID of the +resctrl monitoring domain that is in turn determined by querying the NUMA +node ID of any CPU belonging to the monitoring domain. + +When the monitoring domain is going offline its cpu_mask is empty causing +the NUMA node ID query via cpu_to_node() to be done with "nr_cpu_ids" as +argument resulting in an out-of-bounds access. + +Refactor the limbo handler to skip reading the RMID when the RMID will +just be forced to no longer be dirty in the domain anyway. Add a safety +check to the architecture's RMID reader to protect against this scenario. + +Fixes: e13db55b5a0d ("x86/resctrl: Introduce snc_nodes_per_l3_cache") +Closes: https://sashiko.dev/#/patchset/cover.1780456704.git.reinette.chatre%40intel.com?part=9 +Reported-by: Sashiko +Signed-off-by: Reinette Chatre +Signed-off-by: Borislav Petkov (AMD) +Cc: +Link: https://patch.msgid.link/16137433df42f85013b2f7a53626795cbd6637b9.1781029125.git.reinette.chatre@intel.com +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..c02cab5796 --- /dev/null +++ b/queue-6.6/iio-temperature-ltc2983-fix-n_wires-default-bypassin.patch @@ -0,0 +1,49 @@ +From d37543be961a274a8327328af56fb3f85cae2f2e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 13 Jul 2026 18:45:26 +0300 +Subject: iio: temperature: ltc2983: Fix n_wires default bypassing rotation + check + +From: Liviu Stan + +[ 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 +Cc: +Signed-off-by: Jonathan Cameron +Signed-off-by: Liviu Stan + +[ Liviu Stan: resolve conflict - keep fwnode_handle *ref declaration + which was removed upstream ] + +Signed-off-by: Sasha Levin +--- + 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 index 0000000000..5baf6bb184 --- /dev/null +++ b/queue-6.6/pci-always-lift-2.5gt-s-restriction-in-pcie-failed-l.patch @@ -0,0 +1,134 @@ +From e4d4bd7886101ffcae1eb1febf06fd435fe37442 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +commit 72780f7964684939d7d2f69c348876213b184484 upstream. + +Discard Vendor:Device ID matching in the PCIe failed link retraining quirk +and ignore the link status for the removal of the 2.5GT/s speed clamp, +whether applied by the quirk itself or the firmware earlier on. Revert to +the original target link speed if this final link retraining has failed. + +This is so that link training noise in hot-plug scenarios does not make a +link remain clamped to the 2.5GT/s speed where an event race has led the +quirk to apply the speed clamp for one device, only to leave it in place +for a subsequent device to be plugged in. + +Refer to the Link Capabilities register directly for the maximum link speed +determination so as to streamline backporting. + +Fixes: a89c82249c37 ("PCI: Work around PCIe link training failures") +Signed-off-by: Maciej W. Rozycki +Signed-off-by: Bjorn Helgaas +Tested-by: Alok Tiwari +Cc: stable@vger.kernel.org # v6.5+ +Link: https://patch.msgid.link/alpine.DEB.2.21.2512080331530.49654@angie.orcam.me.uk +[ Update for missing PCIe link speed helpers for 6.6.y. ] +Signed-off-by: Maciej W. Rozycki +Signed-off-by: Sasha Levin +--- + 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 + diff --git a/queue-6.6/series b/queue-6.6/series index 50251058a7..8738a37174 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -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 diff --git a/queue-7.1/series b/queue-7.1/series index 4d6b162880..7ea84725ba 100644 --- a/queue-7.1/series +++ b/queue-7.1/series @@ -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 index 0000000000..1441ed25c9 --- /dev/null +++ b/queue-7.1/wifi-mt76-mt7921-mt7925-fix-null-dereference-in-csa-.patch @@ -0,0 +1,83 @@ +From 78c47051a33bcc1f92da0d4c5f5afc2ffad92830 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ Upstream commit 351dd7d2c80d23e56dcce6faa4e62bea5b0877c7 ] + +This patch is based on a BUG as reported by Bongani Hlope at +https://lore.kernel.org/all/20260502125824.425d7159@bongani-mini.home.org.za/ + +When a channel-switch announcement (CSA) beacon is received, +cfg80211 queues a wiphy work item that eventually calls +mt7921_channel_switch_rx_beacon(). If the station disconnects +(or the channel context is otherwise torn down) between the +time the work is queued and the time it runs, the driver's +dev->new_ctx pointer can already have been cleared to NULL. +mt7921_channel_switch_rx_beacon() then dereferences new_ctx +unconditionally, triggering a NULL pointer dereference at +address 0x0: + + BUG: kernel NULL pointer dereference, address: 0000000000000000 + RIP: 0010:mt7921_channel_switch_rx_beacon+0x1f/0x100 [mt7921_common] + +The same missing guard exists in mt7925_channel_switch_rx_beacon(), +which shares the same code pattern introduced by the same commit. + +Add an early-return NULL check for dev->new_ctx in both +mt7921_channel_switch_rx_beacon() and +mt7925_channel_switch_rx_beacon(). When new_ctx is NULL there is +no pending channel switch to process, so returning immediately is +the correct and safe action. + +Fixes: 8aa2f59260eb ("wifi: mt76: mt7921: introduce CSA support") +Reported-by: Bongani Hlope +Oops-Analysis: http://oops.fenrus.org/reports/lkml/20260502125824.425d7159@bongani-mini.home.org.za/report.html +Link: https://lore.kernel.org/all/20260502125824.425d7159@bongani-mini.home.org.za/ +Signed-off-by: Arjan van de Ven +Cc: linux-wireless@vger.kernel.org +Cc: linux-mediatek@lists.infradead.org +Cc: Felix Fietkau +Cc: Lorenzo Bianconi +Cc: Ryder Lee +Link: https://patch.msgid.link/20260504145107.1329197-1-arjan@linux.intel.com +Signed-off-by: Felix Fietkau +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/mediatek/mt76/mt7921/main.c | 3 +++ + drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++ + 2 files changed, 6 insertions(+) + +diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c +index 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 +