]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 21 Jul 2026 10:07:21 +0000 (12:07 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 21 Jul 2026 10:07:21 +0000 (12:07 +0200)
added patches:
acpi-bus-introduce-devm_acpi_install_notify_handler.patch
acpi-driver-check-acpi_companion-against-null-during-probe.patch
acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch
acpi-nfit-core-fix-possible-deadlock-and-missing-notifications.patch
acpi-nfit-core-use-devm_acpi_install_notify_handler.patch
alsa-hda-tas2781-cancel-async-firmware-request-at-unbind.patch
alsa-scarlett2-allow-selecting-config_set-by-firmware-version.patch
alsa-scarlett2-update-offsets-for-2i2-gen-4-firmware-2417.patch
binder-cache-secctx-size-before-release-zeroes-it.patch
binder-use-list_head-to-initialize-on-stack-list-head.patch
bluetooth-6lowpan-fix-cyclic-locking-warning-on-netdev-unregister.patch
bluetooth-l2cap-fix-use-after-free-in-l2cap_sock_new_connection_cb.patch
bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch
bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch
bpf-consistently-use-reg_state-for-register-access-in-the-verifier.patch
bpf-introduce-struct-bpf_map_desc-in-verifier.patch
bpf-keep-dynamic-inner-array-lookups-nullable.patch
btrfs-concentrate-the-error-handling-of-submit_one_sector.patch
btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch
btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch
btrfs-remove-folio-parameter-from-ordered-io-related-functions.patch
btrfs-replace-for_each_set_bit-with-for_each_set_bitmap.patch
cifs-smb1-split-add-some-includes.patch
cifs-smb1-split-rename-cifstransport.c.patch
crypto-atmel-sha204a-fail-on-hwrng-registration-error-in-probe-path.patch
crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch
crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch
firmware_loader-add-cancel-helper-for-async-requests.patch
functionfs-don-t-abuse-ffs_data_closed-on-fs-shutdown.patch
functionfs-don-t-bother-with-ffs-ref-in-ffs_data_-opened-closed.patch
functionfs-switch-to-simple_remove_by_name.patch
functionfs-use-spinlock-for-ffs_deactivated-ffs_closing-transitions.patch
hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch
hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch
iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch
ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch
ksmbd-fix-path-resolution-in-ksmbd_vfs_kern_path_create.patch
ksmbd-track-the-connection-owning-a-byte-range-lock.patch
ksmbd-use-hmac-md5-library-for-ntlmv2.patch
ksmbd-use-hmac-sha256-library-for-message-signing-and-key-generation.patch
ksmbd-use-opener-credentials-for-fsctl-mutations.patch
ksmbd-use-sha-512-library-for-smb3.1.1-preauth-hash.patch
ksmbd-validate-ntlmv2-response-before-updating-session-key.patch
media-nxp-imx8-isi-fix-use-after-free-on-remove.patch
media-nxp-imx8-isi-use-devm_pm_runtime_enable-to-simplify-code.patch
new-helper-simple_remove_by_name.patch
pci-add-kerneldoc-for-pci_resize_resource.patch
pci-change-pci_dev-variable-from-bridge-to-dev.patch
pci-fix-restoring-bars-on-bar-resize-rollback-path.patch
pci-free-saved-list-without-holding-pci_bus_sem.patch
pci-imx6-configure-ref_use_pad-before-phy-reset-for-i.mx95.patch
pci-imx6-fix-reference-clock-source-selection-for-i.mx95.patch
pci-iov-adjust-barsz-when-changing-bar-size.patch
pci-move-resizable-bar-code-to-rebar.c.patch
pci-skip-resizable-bar-restore-on-read-error.patch
pci-try-bar-resize-even-when-no-window-was-released.patch
proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch
proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch
proc-rename-proc_setattr-to-proc_nochmod_setattr.patch
seqlock-change-do_task_stat-to-use-scoped_seqlock_read.patch
seqlock-introduce-scoped_seqlock_read.patch
staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch
staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch
staging-rtl8723bs-fix-spaces-around-binary-operators.patch
usb-atm-ueagle-atm-remove-function-entry-exit-debug-messages.patch
usb-atm-ueagle-atm-use-dev_dbg-for-device-found-message.patch
usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch
usb-dwc3-fix-dwc3_readl-and-dwc3_writel-calls-in-dwc3_ulpi_setup.patch
usb-dwc3-support-usb3340x-ulpi-phy-high-speed-negotiation.patch
usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch
usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch
vfio-mlx5-fix-racy-bitfields-and-tighten-struct-layout.patch
xfs-add-a-xfs_groups_to_rfsbs-helper.patch
xfs-initialize-iomap-flags-earlier-in-xfs_bmbt_to_iomap.patch
xfs-only-log-freed-extents-for-the-current-rtg-in-zoned-growfs.patch
xfs-use-bio_reuse-in-the-zone-gc-code.patch

77 files changed:
queue-6.18/acpi-bus-introduce-devm_acpi_install_notify_handler.patch [new file with mode: 0644]
queue-6.18/acpi-driver-check-acpi_companion-against-null-during-probe.patch [new file with mode: 0644]
queue-6.18/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch [new file with mode: 0644]
queue-6.18/acpi-nfit-core-fix-possible-deadlock-and-missing-notifications.patch [new file with mode: 0644]
queue-6.18/acpi-nfit-core-use-devm_acpi_install_notify_handler.patch [new file with mode: 0644]
queue-6.18/alsa-hda-tas2781-cancel-async-firmware-request-at-unbind.patch [new file with mode: 0644]
queue-6.18/alsa-scarlett2-allow-selecting-config_set-by-firmware-version.patch [new file with mode: 0644]
queue-6.18/alsa-scarlett2-update-offsets-for-2i2-gen-4-firmware-2417.patch [new file with mode: 0644]
queue-6.18/binder-cache-secctx-size-before-release-zeroes-it.patch [new file with mode: 0644]
queue-6.18/binder-use-list_head-to-initialize-on-stack-list-head.patch [new file with mode: 0644]
queue-6.18/bluetooth-6lowpan-fix-cyclic-locking-warning-on-netdev-unregister.patch [new file with mode: 0644]
queue-6.18/bluetooth-l2cap-fix-use-after-free-in-l2cap_sock_new_connection_cb.patch [new file with mode: 0644]
queue-6.18/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch [new file with mode: 0644]
queue-6.18/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch [new file with mode: 0644]
queue-6.18/bpf-consistently-use-reg_state-for-register-access-in-the-verifier.patch [new file with mode: 0644]
queue-6.18/bpf-introduce-struct-bpf_map_desc-in-verifier.patch [new file with mode: 0644]
queue-6.18/bpf-keep-dynamic-inner-array-lookups-nullable.patch [new file with mode: 0644]
queue-6.18/btrfs-concentrate-the-error-handling-of-submit_one_sector.patch [new file with mode: 0644]
queue-6.18/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch [new file with mode: 0644]
queue-6.18/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch [new file with mode: 0644]
queue-6.18/btrfs-remove-folio-parameter-from-ordered-io-related-functions.patch [new file with mode: 0644]
queue-6.18/btrfs-replace-for_each_set_bit-with-for_each_set_bitmap.patch [new file with mode: 0644]
queue-6.18/cifs-smb1-split-add-some-includes.patch [new file with mode: 0644]
queue-6.18/cifs-smb1-split-rename-cifstransport.c.patch [new file with mode: 0644]
queue-6.18/crypto-atmel-sha204a-fail-on-hwrng-registration-error-in-probe-path.patch [new file with mode: 0644]
queue-6.18/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch [new file with mode: 0644]
queue-6.18/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch [new file with mode: 0644]
queue-6.18/firmware_loader-add-cancel-helper-for-async-requests.patch [new file with mode: 0644]
queue-6.18/functionfs-don-t-abuse-ffs_data_closed-on-fs-shutdown.patch [new file with mode: 0644]
queue-6.18/functionfs-don-t-bother-with-ffs-ref-in-ffs_data_-opened-closed.patch [new file with mode: 0644]
queue-6.18/functionfs-switch-to-simple_remove_by_name.patch [new file with mode: 0644]
queue-6.18/functionfs-use-spinlock-for-ffs_deactivated-ffs_closing-transitions.patch [new file with mode: 0644]
queue-6.18/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch [new file with mode: 0644]
queue-6.18/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch [new file with mode: 0644]
queue-6.18/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch [new file with mode: 0644]
queue-6.18/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch [new file with mode: 0644]
queue-6.18/ksmbd-fix-path-resolution-in-ksmbd_vfs_kern_path_create.patch [new file with mode: 0644]
queue-6.18/ksmbd-track-the-connection-owning-a-byte-range-lock.patch [new file with mode: 0644]
queue-6.18/ksmbd-use-hmac-md5-library-for-ntlmv2.patch [new file with mode: 0644]
queue-6.18/ksmbd-use-hmac-sha256-library-for-message-signing-and-key-generation.patch [new file with mode: 0644]
queue-6.18/ksmbd-use-opener-credentials-for-fsctl-mutations.patch [new file with mode: 0644]
queue-6.18/ksmbd-use-sha-512-library-for-smb3.1.1-preauth-hash.patch [new file with mode: 0644]
queue-6.18/ksmbd-validate-ntlmv2-response-before-updating-session-key.patch [new file with mode: 0644]
queue-6.18/media-nxp-imx8-isi-fix-use-after-free-on-remove.patch [new file with mode: 0644]
queue-6.18/media-nxp-imx8-isi-use-devm_pm_runtime_enable-to-simplify-code.patch [new file with mode: 0644]
queue-6.18/new-helper-simple_remove_by_name.patch [new file with mode: 0644]
queue-6.18/pci-add-kerneldoc-for-pci_resize_resource.patch [new file with mode: 0644]
queue-6.18/pci-change-pci_dev-variable-from-bridge-to-dev.patch [new file with mode: 0644]
queue-6.18/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch [new file with mode: 0644]
queue-6.18/pci-free-saved-list-without-holding-pci_bus_sem.patch [new file with mode: 0644]
queue-6.18/pci-imx6-configure-ref_use_pad-before-phy-reset-for-i.mx95.patch [new file with mode: 0644]
queue-6.18/pci-imx6-fix-reference-clock-source-selection-for-i.mx95.patch [new file with mode: 0644]
queue-6.18/pci-iov-adjust-barsz-when-changing-bar-size.patch [new file with mode: 0644]
queue-6.18/pci-move-resizable-bar-code-to-rebar.c.patch [new file with mode: 0644]
queue-6.18/pci-skip-resizable-bar-restore-on-read-error.patch [new file with mode: 0644]
queue-6.18/pci-try-bar-resize-even-when-no-window-was-released.patch [new file with mode: 0644]
queue-6.18/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch [new file with mode: 0644]
queue-6.18/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch [new file with mode: 0644]
queue-6.18/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch [new file with mode: 0644]
queue-6.18/seqlock-change-do_task_stat-to-use-scoped_seqlock_read.patch [new file with mode: 0644]
queue-6.18/seqlock-introduce-scoped_seqlock_read.patch [new file with mode: 0644]
queue-6.18/series
queue-6.18/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch [new file with mode: 0644]
queue-6.18/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch [new file with mode: 0644]
queue-6.18/staging-rtl8723bs-fix-spaces-around-binary-operators.patch [new file with mode: 0644]
queue-6.18/usb-atm-ueagle-atm-remove-function-entry-exit-debug-messages.patch [new file with mode: 0644]
queue-6.18/usb-atm-ueagle-atm-use-dev_dbg-for-device-found-message.patch [new file with mode: 0644]
queue-6.18/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch [new file with mode: 0644]
queue-6.18/usb-dwc3-fix-dwc3_readl-and-dwc3_writel-calls-in-dwc3_ulpi_setup.patch [new file with mode: 0644]
queue-6.18/usb-dwc3-support-usb3340x-ulpi-phy-high-speed-negotiation.patch [new file with mode: 0644]
queue-6.18/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch [new file with mode: 0644]
queue-6.18/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch [new file with mode: 0644]
queue-6.18/vfio-mlx5-fix-racy-bitfields-and-tighten-struct-layout.patch [new file with mode: 0644]
queue-6.18/xfs-add-a-xfs_groups_to_rfsbs-helper.patch [new file with mode: 0644]
queue-6.18/xfs-initialize-iomap-flags-earlier-in-xfs_bmbt_to_iomap.patch [new file with mode: 0644]
queue-6.18/xfs-only-log-freed-extents-for-the-current-rtg-in-zoned-growfs.patch [new file with mode: 0644]
queue-6.18/xfs-use-bio_reuse-in-the-zone-gc-code.patch [new file with mode: 0644]

diff --git a/queue-6.18/acpi-bus-introduce-devm_acpi_install_notify_handler.patch b/queue-6.18/acpi-bus-introduce-devm_acpi_install_notify_handler.patch
new file mode 100644 (file)
index 0000000..13ce700
--- /dev/null
@@ -0,0 +1,123 @@
+From stable+bounces-273270-greg=kroah.com@vger.kernel.org Fri Jul 10 17:53:54 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 11:47:07 -0400
+Subject: ACPI: bus: Introduce devm_acpi_install_notify_handler()
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260710154710.286956-2-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit ca70ce555a1eb8edf5fb1d5575f1fe19c9ae17f4 ]
+
+Introduce devm_acpi_install_notify_handler() for installing an ACPI
+notify handler managed by devres that will be removed automatically on
+driver detach.
+
+It installs the notify handler on the device object in the ACPI
+namespace that corresponds to the owner device's ACPI companion, if
+present (an error is returned if the owner device doesn't have an ACPI
+companion).
+
+Currently, there is no way to manually remove the notify handler
+installed by it because none of its users brought on subsequently
+will need to do that.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+[ rjw: Kerneldoc comment refinement ]
+Link: https://patch.msgid.link/2268031.irdbgypaU6@rafael.j.wysocki
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Stable-dep-of: 18a00ed0e718 ("ACPI: NFIT: core: Fix possible deadlock and missing notifications")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/bus.c      |   66 ++++++++++++++++++++++++++++++++++++++++++++++++
+ include/acpi/acpi_bus.h |    2 +
+ 2 files changed, 68 insertions(+)
+
+--- a/drivers/acpi/bus.c
++++ b/drivers/acpi/bus.c
+@@ -622,6 +622,72 @@ void acpi_dev_remove_notify_handler(stru
+ }
+ EXPORT_SYMBOL_GPL(acpi_dev_remove_notify_handler);
++struct acpi_notify_handler_devres {
++      acpi_notify_handler handler;
++      u32 handler_type;
++};
++
++static void devm_acpi_notify_handler_release(struct device *dev, void *res)
++{
++      struct acpi_notify_handler_devres *dr = res;
++
++      acpi_dev_remove_notify_handler(ACPI_COMPANION(dev), dr->handler_type,
++                                     dr->handler);
++}
++
++/**
++ * devm_acpi_install_notify_handler - Install an ACPI notify handler for a
++ *                                  managed device
++ * @dev: Device to install a notify handler for
++ * @handler_type: Type of the notify handler
++ * @handler: Handler function to install
++ * @context: Data passed back to the handler function
++ *
++ * This function performs the same function as acpi_dev_install_notify_handler()
++ * called for the ACPI companion of @dev with the same @handler_type, @handler,
++ * and @context arguments, but the ACPI notify handler installed by it will be
++ * automatically removed on driver detach.
++ *
++ * Callers should ensure that all resources used by @handler have been allocated
++ * prior to invoking this function, in which case those resources should be
++ * devres-managed so that they won't be released before the notify handler
++ * removal.  Otherwise, special synchronization between @handler and the
++ * management of those resources is required.
++ *
++ * When the request fails, an error message is printed.  Don't add extra error
++ * messages at the call sites.
++ *
++ * Return: 0 on success or a negative error number.
++ */
++int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type,
++                                   acpi_notify_handler handler, void *context)
++{
++      struct acpi_notify_handler_devres *dr;
++      struct acpi_device *adev;
++      int ret;
++
++      adev = ACPI_COMPANION(dev);
++      if (!adev)
++              return dev_err_probe(dev, -ENODEV, "No ACPI companion in %s()\n", __func__);
++
++      dr = devres_alloc(devm_acpi_notify_handler_release, sizeof(*dr), GFP_KERNEL);
++      if (!dr)
++              return -ENOMEM;
++
++      ret = acpi_dev_install_notify_handler(adev, handler_type, handler, context);
++      if (ret) {
++              devres_free(dr);
++              return dev_err_probe(dev, ret, "Failed to install an ACPI notify handler\n");
++      }
++
++      dr->handler = handler;
++      dr->handler_type = handler_type;
++      devres_add(dev, dr);
++
++      return 0;
++}
++EXPORT_SYMBOL_GPL(devm_acpi_install_notify_handler);
++
+ /* Handle events targeting \_SB device (at present only graceful shutdown) */
+ #define ACPI_SB_NOTIFY_SHUTDOWN_REQUEST 0x81
+--- a/include/acpi/acpi_bus.h
++++ b/include/acpi/acpi_bus.h
+@@ -625,6 +625,8 @@ int acpi_dev_install_notify_handler(stru
+ void acpi_dev_remove_notify_handler(struct acpi_device *adev,
+                                   u32 handler_type,
+                                   acpi_notify_handler handler);
++int devm_acpi_install_notify_handler(struct device *dev, u32 handler_type,
++                                   acpi_notify_handler handler, void *context);
+ extern int acpi_notifier_call_chain(struct acpi_device *, u32, u32);
+ extern int register_acpi_notifier(struct notifier_block *);
+ extern int unregister_acpi_notifier(struct notifier_block *);
diff --git a/queue-6.18/acpi-driver-check-acpi_companion-against-null-during-probe.patch b/queue-6.18/acpi-driver-check-acpi_companion-against-null-during-probe.patch
new file mode 100644 (file)
index 0000000..da401c3
--- /dev/null
@@ -0,0 +1,144 @@
+From stable+bounces-273269-greg=kroah.com@vger.kernel.org Fri Jul 10 17:53:46 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 11:47:06 -0400
+Subject: ACPI: driver: Check ACPI_COMPANION() against NULL during probe
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Hans de Goede <johannes.goede@oss.qualcomm.com>, Andy Shevchenko <andriy.shevchenko@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260710154710.286956-1-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit e4865a56d013e86e46ea6acea15bb6eae01898ff ]
+
+Since every platform driver can be forced to match a device that doesn't
+match its list of device IDs because of device_match_driver_override(),
+platform drivers that rely on the existence of a device's ACPI companion
+object should verify its presence.
+
+Accordingly, add requisite ACPI_COMPANION() or ACPI_HANDLE() checks
+against NULL to 13 platform drivers handling core ACPI devices.
+
+Also change the value returned by the ACPI thermal zone driver when
+the device's ACPI companion is not present to -ENODEV for consistency
+with the other drivers.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Reviewed-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://patch.msgid.link/4516068.ejJDZkT8p0@rafael.j.wysocki
+Cc: 7.0+ <stable@vger.kernel.org> # 7.0+
+Stable-dep-of: 18a00ed0e718 ("ACPI: NFIT: core: Fix possible deadlock and missing notifications")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/ac.c            |    6 +++++-
+ drivers/acpi/acpi_pad.c      |    6 +++++-
+ drivers/acpi/acpi_tad.c      |    6 +++++-
+ drivers/acpi/pfr_telemetry.c |    6 +++++-
+ drivers/acpi/pfr_update.c    |    6 +++++-
+ drivers/acpi/thermal.c       |    2 +-
+ 6 files changed, 26 insertions(+), 6 deletions(-)
+
+--- a/drivers/acpi/ac.c
++++ b/drivers/acpi/ac.c
+@@ -203,11 +203,15 @@ static const struct dmi_system_id ac_dmi
+ static int acpi_ac_probe(struct platform_device *pdev)
+ {
+-      struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+       struct power_supply_config psy_cfg = {};
++      struct acpi_device *adev;
+       struct acpi_ac *ac;
+       int result;
++      adev = ACPI_COMPANION(&pdev->dev);
++      if (!adev)
++              return -ENODEV;
++
+       ac = kzalloc(sizeof(struct acpi_ac), GFP_KERNEL);
+       if (!ac)
+               return -ENOMEM;
+--- a/drivers/acpi/acpi_pad.c
++++ b/drivers/acpi/acpi_pad.c
+@@ -426,9 +426,13 @@ static void acpi_pad_notify(acpi_handle
+ static int acpi_pad_probe(struct platform_device *pdev)
+ {
+-      struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
++      struct acpi_device *adev;
+       acpi_status status;
++      adev = ACPI_COMPANION(&pdev->dev);
++      if (!adev)
++              return -ENODEV;
++
+       strscpy(acpi_device_name(adev), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME);
+       strscpy(acpi_device_class(adev), ACPI_PROCESSOR_AGGREGATOR_CLASS);
+--- a/drivers/acpi/acpi_tad.c
++++ b/drivers/acpi/acpi_tad.c
+@@ -588,12 +588,16 @@ static void acpi_tad_remove(struct platf
+ static int acpi_tad_probe(struct platform_device *pdev)
+ {
+       struct device *dev = &pdev->dev;
+-      acpi_handle handle = ACPI_HANDLE(dev);
+       struct acpi_tad_driver_data *dd;
++      acpi_handle handle;
+       acpi_status status;
+       unsigned long long caps;
+       int ret;
++      handle = ACPI_HANDLE(dev);
++      if (!handle)
++              return -ENODEV;
++
+       ret = acpi_install_cmos_rtc_space_handler(handle);
+       if (ret < 0) {
+               dev_info(dev, "Unable to install space handler\n");
+--- a/drivers/acpi/pfr_telemetry.c
++++ b/drivers/acpi/pfr_telemetry.c
+@@ -360,10 +360,14 @@ static void pfrt_log_put_idx(void *data)
+ static int acpi_pfrt_log_probe(struct platform_device *pdev)
+ {
+-      acpi_handle handle = ACPI_HANDLE(&pdev->dev);
+       struct pfrt_log_device *pfrt_log_dev;
++      acpi_handle handle;
+       int ret;
++      handle = ACPI_HANDLE(&pdev->dev);
++      if (!handle)
++              return -ENODEV;
++
+       if (!acpi_has_method(handle, "_DSM")) {
+               dev_dbg(&pdev->dev, "Missing _DSM\n");
+               return -ENODEV;
+--- a/drivers/acpi/pfr_update.c
++++ b/drivers/acpi/pfr_update.c
+@@ -538,10 +538,14 @@ static void pfru_put_idx(void *data)
+ static int acpi_pfru_probe(struct platform_device *pdev)
+ {
+-      acpi_handle handle = ACPI_HANDLE(&pdev->dev);
+       struct pfru_device *pfru_dev;
++      acpi_handle handle;
+       int ret;
++      handle = ACPI_HANDLE(&pdev->dev);
++      if (!handle)
++              return -ENODEV;
++
+       if (!acpi_has_method(handle, "_DSM")) {
+               dev_dbg(&pdev->dev, "Missing _DSM\n");
+               return -ENODEV;
+--- a/drivers/acpi/thermal.c
++++ b/drivers/acpi/thermal.c
+@@ -789,7 +789,7 @@ static int acpi_thermal_add(struct acpi_
+       int i;
+       if (!device)
+-              return -EINVAL;
++              return -ENODEV;
+       tz = kzalloc(sizeof(struct acpi_thermal), GFP_KERNEL);
+       if (!tz)
diff --git a/queue-6.18/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch b/queue-6.18/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch
new file mode 100644 (file)
index 0000000..7e47dab
--- /dev/null
@@ -0,0 +1,110 @@
+From stable+bounces-273237-greg=kroah.com@vger.kernel.org Fri Jul 10 15:33:07 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 09:32:35 -0400
+Subject: ACPI: NFIT: core: Fix acpi_nfit_init() error cleanup
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Dave Jiang <dave.jiang@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260710133235.81141-1-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 38bf27511ef41bffebd157ec3eba41fc89ba59cd ]
+
+If acpi_nfit_init() fails after adding the acpi_desc object to the
+acpi_descs list, that object is never removed from that list because
+the acpi_nfit_shutdown() devm action is not added for the NFIT device
+in that case.  Next, the acpi_nfit_init() failure causes
+acpi_nfit_probe() to fail, the acpi_desc object is freed, and a
+dangling pointer is left behind in the acpi_descs.  Any subsequent
+ACPI Machine Check Exception will trigger nfit_handle_mce() which
+iterates over acpi_descs and so a use-after-free will occur.
+
+Moreover, if acpi_nfit_probe() returns 0 after installing a notify
+handler for the NFIT device and without allocating the acpi_desc
+object and setting the NFIT device's driver data pointer, the
+acpi_desc object will be allocated by acpi_nfit_update_notify()
+and acpi_nfit_init() will be called to initialize it.  Regardless
+of whether or not acpi_nfit_init() fails in that case, the
+acpi_nfit_shutdown() devm action is not added for the NFIT device
+and acpi_desc is never removed from the acpi_descs list.  If the
+acpi_desc object is freed subsequently on driver removal, any
+subsequent ACPI MCE will lead to a use-after-free like in the
+previous case.
+
+To address the first issue mentioned above, make acpi_nfit_probe()
+call acpi_nfit_shutdown() directly on acpi_nfit_init() failures and
+to address the other one, add a remove callback to the driver and
+make it call acpi_nfit_shutdown().  Also, since it is now possible to
+pass NULL to acpi_nfit_shutdown() or the acpi_desc object passed to it
+may not have been initialized, add checks against NULL for acpi_desc and
+its nvdimm_bus field to that function and make acpi_nfit_unregister()
+clear the latter after unregistering the NVDIMM bus.
+
+Fixes: a61fe6f7902e ("nfit, tools/testing/nvdimm: unify common init for acpi_nfit_desc")
+Fixes: fbabd829fe76 ("acpi, nfit: fix module unload vs workqueue shutdown race")
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Cc: All applicable <stable@vger.kernel.org>
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Link: https://patch.msgid.link/1963615.tdWV9SEqCh@rafael.j.wysocki
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/nfit/core.c |   18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/drivers/acpi/nfit/core.c
++++ b/drivers/acpi/nfit/core.c
+@@ -3061,6 +3061,8 @@ static void acpi_nfit_unregister(void *d
+       struct acpi_nfit_desc *acpi_desc = data;
+       nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
++      /* The nvdimm_bus object may have been freed, so clear the pointer. */
++      acpi_desc->nvdimm_bus = NULL;
+ }
+ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz)
+@@ -3301,7 +3303,10 @@ static void acpi_nfit_remove_notify_hand
+ void acpi_nfit_shutdown(void *data)
+ {
+       struct acpi_nfit_desc *acpi_desc = data;
+-      struct device *bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
++      struct device *bus_dev;
++
++      if (!acpi_desc || !acpi_desc->nvdimm_bus)
++              return;
+       /*
+        * Destruct under acpi_desc_lock so that nfit_handle_mce does not
+@@ -3316,6 +3321,7 @@ void acpi_nfit_shutdown(void *data)
+       mutex_unlock(&acpi_desc->init_mutex);
+       cancel_delayed_work_sync(&acpi_desc->dwork);
++      bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
+       /*
+        * Bounce the nvdimm bus lock to make sure any in-flight
+        * acpi_nfit_ars_rescan() submissions have had a chance to
+@@ -3393,9 +3399,14 @@ static int acpi_nfit_add(struct acpi_dev
+                               sz - sizeof(struct acpi_table_nfit));
+       if (rc)
+-              return rc;
++              acpi_nfit_shutdown(acpi_desc);
+-      return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc);
++      return rc;
++}
++
++static void acpi_nfit_remove(struct acpi_device *adev)
++{
++      acpi_nfit_shutdown(dev_get_drvdata(&adev->dev));
+ }
+ static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle)
+@@ -3482,6 +3493,7 @@ static struct acpi_driver acpi_nfit_driv
+       .ids = acpi_nfit_ids,
+       .ops = {
+               .add = acpi_nfit_add,
++              .remove = acpi_nfit_remove,
+       },
+ };
diff --git a/queue-6.18/acpi-nfit-core-fix-possible-deadlock-and-missing-notifications.patch b/queue-6.18/acpi-nfit-core-fix-possible-deadlock-and-missing-notifications.patch
new file mode 100644 (file)
index 0000000..d85a172
--- /dev/null
@@ -0,0 +1,222 @@
+From stable+bounces-273273-greg=kroah.com@vger.kernel.org Fri Jul 10 17:47:23 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 11:47:10 -0400
+Subject: ACPI: NFIT: core: Fix possible deadlock and missing notifications
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Dave Jiang <dave.jiang@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260710154710.286956-5-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 18a00ed0e718473f5c3fcfa49df46c944575e60c ]
+
+After commit 9b311b7313d6 ("ACPI: NFIT: Install Notify() handler before
+getting NFIT table"), ACPI NFIT driver removal may deadlock if an ACPI
+notify on the NFIT device is triggered concurrently.  A similar deadlock
+may occur if an ACPI notify on the NFIT device is triggered during a
+failing driver probe.
+
+The deadlock is possible because acpi_dev_remove_notify_handler() calls
+acpi_os_wait_events_complete() after removing the notify handler and the
+driver core invokes it under the NFIT platform device lock which is also
+acquired by acpi_nfit_notify().  Thus acpi_os_wait_events_complete() may
+be waiting for acpi_nfit_notify() to complete, but the latter may not be
+able to acquire the device lock which is being held by the driver core
+while the former is being executed.
+
+Moreover, after commit 03667e146f81 ("ACPI: NFIT: core: Convert the
+driver to a platform one"), there are no sysfs notifications regarding
+NVDIMM devices because __acpi_nvdimm_notify() always bails out after
+checking the driver data pointer of the device's parent.  That parent
+is the ACPI companion of the platform device used for driver binding,
+so its driver data pointer is always NULL after the commit in question
+which was overlooked by it.
+
+A remedy for the deadlock is to use a special separate lock for ACPI
+notify synchronization with driver probe and removal instead of the
+device lock of the NFIT device, while a remedy for the second issue
+is to populate the driver data pointer of the NFIT device's ACPI
+companion when the driver is ready to operate, so do both these things.
+However, since the new lock is not held across the entire teardown and
+acpi_nfit_notify() should do nothing when teardown is in progress, make
+it check the driver data pointer of the NFIT device's ACPI companion, in
+analogy with the existing check in __acpi_nvdimm_notify(), and bail out
+if that pointer is NULL.
+
+Fixes: 9b311b7313d6 ("ACPI: NFIT: Install Notify() handler before getting NFIT table")
+Fixes: 03667e146f81 ("ACPI: NFIT: core: Convert the driver to a platform one")
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Cc: All applicable <stable@vger.kernel.org> # 9995e4404ea4: ACPI: NFIT: core: Eliminate redundant local variable
+Reviewed-by: Dave Jiang <dave.jiang@intel.com>
+Link: https://patch.msgid.link/3420096.aeNJFYEL58@rafael.j.wysocki
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/bus.c       |    6 +++-
+ drivers/acpi/nfit/core.c |   62 +++++++++++++++++++++++++++++++++++++----------
+ 2 files changed, 54 insertions(+), 14 deletions(-)
+
+--- a/drivers/acpi/bus.c
++++ b/drivers/acpi/bus.c
+@@ -623,6 +623,7 @@ void acpi_dev_remove_notify_handler(stru
+ EXPORT_SYMBOL_GPL(acpi_dev_remove_notify_handler);
+ struct acpi_notify_handler_devres {
++      struct acpi_device *adev;
+       acpi_notify_handler handler;
+       u32 handler_type;
+ };
+@@ -631,7 +632,7 @@ static void devm_acpi_notify_handler_rel
+ {
+       struct acpi_notify_handler_devres *dr = res;
+-      acpi_dev_remove_notify_handler(ACPI_COMPANION(dev), dr->handler_type,
++      acpi_dev_remove_notify_handler(dr->adev, dr->handler_type,
+                                      dr->handler);
+ }
+@@ -667,6 +668,8 @@ int devm_acpi_install_notify_handler(str
+       int ret;
+       adev = ACPI_COMPANION(dev);
++      if (!adev && dev->bus == &acpi_bus_type)
++              adev = to_acpi_device(dev);
+       if (!adev)
+               return dev_err_probe(dev, -ENODEV, "No ACPI companion in %s()\n", __func__);
+@@ -680,6 +683,7 @@ int devm_acpi_install_notify_handler(str
+               return dev_err_probe(dev, ret, "Failed to install an ACPI notify handler\n");
+       }
++      dr->adev = adev;
+       dr->handler = handler;
+       dr->handler_type = handler_type;
+       devres_add(dev, dr);
+--- a/drivers/acpi/nfit/core.c
++++ b/drivers/acpi/nfit/core.c
+@@ -55,6 +55,8 @@ MODULE_PARM_DESC(force_labels, "Opt-in t
+ LIST_HEAD(acpi_descs);
+ DEFINE_MUTEX(acpi_desc_lock);
++DEFINE_MUTEX(acpi_notify_lock);
++
+ static struct workqueue_struct *nfit_wq;
+ struct nfit_table_prev {
+@@ -1702,9 +1704,15 @@ static void acpi_nvdimm_notify(acpi_hand
+       struct acpi_device *adev = data;
+       struct device *dev = &adev->dev;
+-      device_lock(dev->parent);
+-      __acpi_nvdimm_notify(dev, event);
+-      device_unlock(dev->parent);
++      /*
++       * Locking is needed here for synchronization with driver probe and
++       * removal and the parent NFIT device's ACPI driver data pointer is
++       * NULL when teardown is in progress.
++       */
++      guard(mutex)(&acpi_notify_lock);
++
++      if (acpi_driver_data(to_acpi_device(dev->parent)))
++              __acpi_nvdimm_notify(dev, event);
+ }
+ static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
+@@ -3150,11 +3158,10 @@ EXPORT_SYMBOL_GPL(acpi_nfit_init);
+ static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc)
+ {
+       struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
+-      struct device *dev = acpi_desc->dev;
+-      /* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */
+-      device_lock(dev);
+-      device_unlock(dev);
++      /* Bounce the notify lock to flush acpi_nfit_add / acpi_nfit_notify */
++      mutex_lock(&acpi_notify_lock);
++      mutex_unlock(&acpi_notify_lock);
+       /* Bounce the init_mutex to complete initial registration */
+       mutex_lock(&acpi_desc->init_mutex);
+@@ -3286,10 +3293,17 @@ static void acpi_nfit_put_table(void *ta
+ static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data)
+ {
+       struct device *dev = data;
++      struct acpi_device *adev = to_acpi_device(dev);
+-      device_lock(dev);
+-      __acpi_nfit_notify(dev, handle, event);
+-      device_unlock(dev);
++      /*
++       * Locking is needed here for synchronization with driver probe and
++       * removal and the ACPI driver data pointer is NULL when teardown
++       * is in progress.
++       */
++      guard(mutex)(&acpi_notify_lock);
++
++      if (acpi_driver_data(adev))
++              __acpi_nfit_notify(dev, handle, event);
+ }
+ void acpi_nfit_shutdown(void *data)
+@@ -3336,6 +3350,12 @@ static int acpi_nfit_add(struct acpi_dev
+       acpi_size sz;
+       int rc = 0;
++      /*
++       * Prevent acpi_nfit_notify() from progressing until the probe is
++       * complete in case there is a concurrent event to process.
++       */
++      guard(mutex)(&acpi_notify_lock);
++
+       rc = devm_acpi_install_notify_handler(dev, ACPI_DEVICE_NOTIFY,
+                                             acpi_nfit_notify, dev);
+       if (rc)
+@@ -3351,6 +3371,11 @@ static int acpi_nfit_add(struct acpi_dev
+                * data in the format of a series of NFIT Structures.
+                */
+               dev_dbg(dev, "failed to find NFIT at startup\n");
++              /*
++               * Let acpi_nfit_update_notify() run in case it will need to
++               * allocate the acpi_desc object.
++               */
++              adev->driver_data = dev;
+               return 0;
+       }
+@@ -3368,7 +3393,7 @@ static int acpi_nfit_add(struct acpi_dev
+       acpi_desc->acpi_header = *tbl;
+       /* Evaluate _FIT and override with that if present */
+-      status = acpi_evaluate_object(ACPI_HANDLE(dev), "_FIT", NULL, &buf);
++      status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf);
+       if (ACPI_SUCCESS(status) && buf.length > 0) {
+               union acpi_object *obj = buf.pointer;
+@@ -3385,14 +3410,25 @@ static int acpi_nfit_add(struct acpi_dev
+                               + sizeof(struct acpi_table_nfit),
+                               sz - sizeof(struct acpi_table_nfit));
+-      if (rc)
++      if (rc) {
+               acpi_nfit_shutdown(acpi_desc);
++              return rc;
++      }
+-      return rc;
++      /*
++       * Let notify handlers operate (the actual value of the ACPI driver
++       * data pointer does not matter here so long as it is not NULL).
++       */
++      adev->driver_data = dev;
++      return 0;
+ }
+ static void acpi_nfit_remove(struct acpi_device *adev)
+ {
++      guard(mutex)(&acpi_notify_lock);
++
++      /* Make notify handlers bail out early going forward. */
++      adev->driver_data = NULL;
+       acpi_nfit_shutdown(dev_get_drvdata(&adev->dev));
+ }
diff --git a/queue-6.18/acpi-nfit-core-use-devm_acpi_install_notify_handler.patch b/queue-6.18/acpi-nfit-core-use-devm_acpi_install_notify_handler.patch
new file mode 100644 (file)
index 0000000..18e49ff
--- /dev/null
@@ -0,0 +1,83 @@
+From stable+bounces-273271-greg=kroah.com@vger.kernel.org Fri Jul 10 17:53:59 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 Jul 2026 11:47:08 -0400
+Subject: ACPI: NFIT: core: Use devm_acpi_install_notify_handler()
+To: stable@vger.kernel.org
+Cc: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260710154710.286956-3-sashal@kernel.org>
+
+From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 198541ad53c0d0d891fedea4098f9953a0f566c0 ]
+
+Now that devm_acpi_install_notify_handler() is available, use it in
+acpi_nfit_probe() instead of a custom devm action removing an ACPI
+notify handler installed via acpi_dev_install_notify_handler().
+
+Also drop the explicit ACPI_COMPANION() check against NULL that is
+not necessary any more becuase devm_acpi_install_notify_handler()
+carries out an equivalent check internally and use ACPI_HANDLE() to
+retrieve the platform device's ACPI handle.
+
+No intentional functional impact.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Link: https://patch.msgid.link/3048737.e9J7NaK4W3@rafael.j.wysocki
+Stable-dep-of: 18a00ed0e718 ("ACPI: NFIT: core: Fix possible deadlock and missing notifications")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/acpi/nfit/core.c |   27 +++++++--------------------
+ 1 file changed, 7 insertions(+), 20 deletions(-)
+
+--- a/drivers/acpi/nfit/core.c
++++ b/drivers/acpi/nfit/core.c
+@@ -3285,19 +3285,11 @@ static void acpi_nfit_put_table(void *ta
+ static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data)
+ {
+-      struct acpi_device *adev = data;
++      struct device *dev = data;
+-      device_lock(&adev->dev);
+-      __acpi_nfit_notify(&adev->dev, handle, event);
+-      device_unlock(&adev->dev);
+-}
+-
+-static void acpi_nfit_remove_notify_handler(void *data)
+-{
+-      struct acpi_device *adev = data;
+-
+-      acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY,
+-                                     acpi_nfit_notify);
++      device_lock(dev);
++      __acpi_nfit_notify(dev, handle, event);
++      device_unlock(dev);
+ }
+ void acpi_nfit_shutdown(void *data)
+@@ -3344,13 +3336,8 @@ static int acpi_nfit_add(struct acpi_dev
+       acpi_size sz;
+       int rc = 0;
+-      rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY,
+-                                           acpi_nfit_notify, adev);
+-      if (rc)
+-              return rc;
+-
+-      rc = devm_add_action_or_reset(dev, acpi_nfit_remove_notify_handler,
+-                                      adev);
++      rc = devm_acpi_install_notify_handler(dev, ACPI_DEVICE_NOTIFY,
++                                            acpi_nfit_notify, dev);
+       if (rc)
+               return rc;
+@@ -3381,7 +3368,7 @@ static int acpi_nfit_add(struct acpi_dev
+       acpi_desc->acpi_header = *tbl;
+       /* Evaluate _FIT and override with that if present */
+-      status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf);
++      status = acpi_evaluate_object(ACPI_HANDLE(dev), "_FIT", NULL, &buf);
+       if (ACPI_SUCCESS(status) && buf.length > 0) {
+               union acpi_object *obj = buf.pointer;
diff --git a/queue-6.18/alsa-hda-tas2781-cancel-async-firmware-request-at-unbind.patch b/queue-6.18/alsa-hda-tas2781-cancel-async-firmware-request-at-unbind.patch
new file mode 100644 (file)
index 0000000..e615950
--- /dev/null
@@ -0,0 +1,65 @@
+From stable+bounces-274118-greg=kroah.com@vger.kernel.org Tue Jul 14 06:05:49 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 00:05:39 -0400
+Subject: ALSA: hda/tas2781: Cancel async firmware request at unbind
+To: stable@vger.kernel.org
+Cc: "Cássio Gabriel" <cassiogabrielcontato@gmail.com>, "Takashi Iwai" <tiwai@suse.de>, "Danilo Krummrich" <dakr@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714040539.2433035-2-sashal@kernel.org>
+
+From: Cássio Gabriel <cassiogabrielcontato@gmail.com>
+
+[ Upstream commit 5367e2ad14f0ae9350a7aaf2e77c87de39a43ae9 ]
+
+TAS2781 HDA I2C and SPI queue RCA firmware loading from component
+bind with request_firmware_nowait(). The firmware loader keeps the
+callback module pinned and holds a device reference, but the callback
+still uses driver-private HDA state.
+
+Component unbind removes controls and DSP state immediately. Later
+device removal tears down the TAS2781 private data, including
+codec_lock. If the async firmware callback runs after unbind has
+started, it can operate on state that is being torn down.
+
+Cancel or synchronize the async firmware request before removing
+controls and DSP state. A queued callback is cancelled, and an
+already-running callback is allowed to finish before unbind continues.
+
+Fixes: 5be27f1e3ec9 ("ALSA: hda/tas2781: Add tas2781 HDA driver")
+Fixes: bb5f86ea50ff ("ALSA: hda/tas2781: Add tas2781 hda SPI driver")
+Cc: stable@vger.kernel.org
+Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
+Reviewed-by: Takashi Iwai <tiwai@suse.de>
+Acked-by: Danilo Krummrich <dakr@kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260505-alsa-hda-tas2781-fw-callback-teardown-v4-2-e7c4bf930dc8@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/hda/codecs/side-codecs/tas2781_hda_i2c.c |    3 +++
+ sound/hda/codecs/side-codecs/tas2781_hda_spi.c |    3 +++
+ 2 files changed, 6 insertions(+)
+
+--- a/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
++++ b/sound/hda/codecs/side-codecs/tas2781_hda_i2c.c
+@@ -616,6 +616,9 @@ static void tas2781_hda_unbind(struct de
+               comp->playback_hook = NULL;
+       }
++      request_firmware_nowait_cancel(tas_hda->priv->dev, tas_hda->priv,
++                                     tasdev_fw_ready);
++
+       tas2781_hda_remove_controls(tas_hda);
+       tasdevice_config_info_remove(tas_hda->priv);
+--- a/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
++++ b/sound/hda/codecs/side-codecs/tas2781_hda_spi.c
+@@ -753,6 +753,9 @@ static void tas2781_hda_unbind(struct de
+               comp->playback_hook = NULL;
+       }
++      request_firmware_nowait_cancel(tas_priv->dev, tas_priv,
++                                     tasdev_fw_ready);
++
+       tas2781_hda_remove_controls(tas_hda);
+       tasdevice_config_info_remove(tas_priv);
diff --git a/queue-6.18/alsa-scarlett2-allow-selecting-config_set-by-firmware-version.patch b/queue-6.18/alsa-scarlett2-allow-selecting-config_set-by-firmware-version.patch
new file mode 100644 (file)
index 0000000..92cf9ee
--- /dev/null
@@ -0,0 +1,340 @@
+From stable+bounces-274082-greg=kroah.com@vger.kernel.org Tue Jul 14 04:10:15 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 22:09:13 -0400
+Subject: ALSA: scarlett2: Allow selecting config_set by firmware version
+To: stable@vger.kernel.org
+Cc: "Geoffrey D. Bennett" <g@b4.vu>, Takashi Iwai <tiwai@suse.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714020914.2356076-1-sashal@kernel.org>
+
+From: "Geoffrey D. Bennett" <g@b4.vu>
+
+[ Upstream commit 732a6397a526c025cd29c3c9309b0db6a2c08837 ]
+
+The Scarlett 2i2 Gen 4 firmware 2417 moved the direct monitor gain
+parameters, so we now need to allow each device to list multiple
+scarlett2_config_set entries, one per applicable firmware version
+range, and pick the matching one at probe time.
+
+No functional change yet: each device gets a single config_sets
+entry whose from_firmware_version matches the existing
+min_firmware_version (0 where none was set). This both prepares for
+selection and lets a follow-up commit remove the now-redundant
+min_firmware_version field.
+
+scarlett2_count_io() depends on the resolved config_set so it moves
+out of scarlett2_init_private() into snd_scarlett2_controls_create()
+after the firmware version has been read.
+
+Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/ae1695b4c4825f365b4c86b22174035f742807e3.1777151532.git.g@b4.vu
+Stable-dep-of: 3ca15754b561 ("ALSA: scarlett2: Update offsets for 2i2 Gen 4 firmware 2417")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/usb/mixer_scarlett2.c |  143 +++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 121 insertions(+), 22 deletions(-)
+
+--- a/sound/usb/mixer_scarlett2.c
++++ b/sound/usb/mixer_scarlett2.c
+@@ -612,6 +612,20 @@ struct scarlett2_config_set {
+       const struct scarlett2_config items[SCARLETT2_CONFIG_COUNT];
+ };
++/* Map firmware versions to config sets per-device.
++ *
++ * Each device lists one or more entries, sorted in ascending order of
++ * from_firmware_version. At probe time the running firmware version
++ * is looked up against this list and the last entry whose
++ * from_firmware_version is <= the running version is selected.
++ *
++ * The list is terminated by a sentinel entry with config_set == NULL.
++ */
++struct scarlett2_config_set_entry {
++      u16 from_firmware_version;
++      const struct scarlett2_config_set *config_set;
++};
++
+ /* Input gain TLV dB ranges */
+ static const DECLARE_TLV_DB_MINMAX(
+@@ -1100,8 +1114,8 @@ struct scarlett2_meter_entry {
+ };
+ struct scarlett2_device_info {
+-      /* which set of configuration parameters the device uses */
+-      const struct scarlett2_config_set *config_set;
++      /* which sets of configuration parameters the device uses */
++      const struct scarlett2_config_set_entry *config_sets;
+       /* minimum firmware version required */
+       u16 min_firmware_version;
+@@ -1343,7 +1357,10 @@ struct scarlett2_data {
+ /*** Model-specific data ***/
+ static const struct scarlett2_device_info s6i6_gen2_info = {
+-      .config_set = &scarlett2_config_set_gen2a,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen2a },
++              { }
++      },
+       .level_input_count = 2,
+       .pad_input_count = 2,
+@@ -1393,7 +1410,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info s18i8_gen2_info = {
+-      .config_set = &scarlett2_config_set_gen2a,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen2a },
++              { }
++      },
+       .level_input_count = 2,
+       .pad_input_count = 4,
+@@ -1446,7 +1466,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info s18i20_gen2_info = {
+-      .config_set = &scarlett2_config_set_gen2b,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen2b },
++              { }
++      },
+       .line_out_descrs = {
+               "Monitor L",
+@@ -1503,7 +1526,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info solo_gen3_info = {
+-      .config_set = &scarlett2_config_set_gen3a,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen3a },
++              { }
++      },
+       .level_input_count = 1,
+       .level_input_first = 1,
+       .air_input_count = 1,
+@@ -1513,7 +1539,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info s2i2_gen3_info = {
+-      .config_set = &scarlett2_config_set_gen3a,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen3a },
++              { }
++      },
+       .level_input_count = 2,
+       .air_input_count = 2,
+       .phantom_count = 1,
+@@ -1522,7 +1551,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info s4i4_gen3_info = {
+-      .config_set = &scarlett2_config_set_gen3b,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen3b },
++              { }
++      },
+       .level_input_count = 2,
+       .pad_input_count = 2,
+       .air_input_count = 2,
+@@ -1571,7 +1603,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info s8i6_gen3_info = {
+-      .config_set = &scarlett2_config_set_gen3b,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen3b },
++              { }
++      },
+       .level_input_count = 2,
+       .pad_input_count = 2,
+       .air_input_count = 2,
+@@ -1637,7 +1672,10 @@ static const char * const scarlett2_spdi
+ };
+ static const struct scarlett2_device_info s18i8_gen3_info = {
+-      .config_set = &scarlett2_config_set_gen3c,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen3c },
++              { }
++      },
+       .has_speaker_switching = 1,
+       .level_input_count = 2,
+       .pad_input_count = 4,
+@@ -1729,7 +1767,10 @@ static const char * const scarlett2_spdi
+ };
+ static const struct scarlett2_device_info s18i20_gen3_info = {
+-      .config_set = &scarlett2_config_set_gen3c,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_gen3c },
++              { }
++      },
+       .has_speaker_switching = 1,
+       .has_talkback = 1,
+       .level_input_count = 2,
+@@ -1803,7 +1844,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info vocaster_one_info = {
+-      .config_set = &scarlett2_config_set_vocaster,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 1769, &scarlett2_config_set_vocaster },
++              { }
++      },
+       .min_firmware_version = 1769,
+       .has_devmap = 1,
+@@ -1847,7 +1891,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info vocaster_two_info = {
+-      .config_set = &scarlett2_config_set_vocaster,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 1769, &scarlett2_config_set_vocaster },
++              { }
++      },
+       .min_firmware_version = 1769,
+       .has_devmap = 1,
+@@ -1892,7 +1939,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info solo_gen4_info = {
+-      .config_set = &scarlett2_config_set_gen4_solo,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 2115, &scarlett2_config_set_gen4_solo },
++              { }
++      },
+       .min_firmware_version = 2115,
+       .has_devmap = 1,
+@@ -1947,7 +1997,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info s2i2_gen4_info = {
+-      .config_set = &scarlett2_config_set_gen4_2i2,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 2115, &scarlett2_config_set_gen4_2i2 },
++              { }
++      },
+       .min_firmware_version = 2115,
+       .has_devmap = 1,
+@@ -2002,7 +2055,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info s4i4_gen4_info = {
+-      .config_set = &scarlett2_config_set_gen4_4i4,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 2089, &scarlett2_config_set_gen4_4i4 },
++              { }
++      },
+       .min_firmware_version = 2089,
+       .has_devmap = 1,
+@@ -2051,7 +2107,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info clarett_2pre_info = {
+-      .config_set = &scarlett2_config_set_clarett,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_clarett },
++              { }
++      },
+       .level_input_count = 2,
+       .air_input_count = 2,
+@@ -2107,7 +2166,10 @@ static const char * const scarlett2_spdi
+ };
+ static const struct scarlett2_device_info clarett_4pre_info = {
+-      .config_set = &scarlett2_config_set_clarett,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_clarett },
++              { }
++      },
+       .level_input_count = 2,
+       .air_input_count = 4,
+@@ -2163,7 +2225,10 @@ static const struct scarlett2_device_inf
+ };
+ static const struct scarlett2_device_info clarett_8pre_info = {
+-      .config_set = &scarlett2_config_set_clarett,
++      .config_sets = (const struct scarlett2_config_set_entry[]) {
++              { 0, &scarlett2_config_set_clarett },
++              { }
++      },
+       .level_input_count = 2,
+       .air_input_count = 8,
+@@ -8211,10 +8276,32 @@ static void scarlett2_private_suspend(st
+ /*** Initialisation ***/
++/* Select the config_set matching the running firmware version.
++ *
++ * The device info's config_sets array is ordered by ascending
++ * from_firmware_version; pick the last entry whose version is <= the
++ * running firmware version. If the running firmware is older than the
++ * first entry's from_firmware_version (i.e. older than the driver's
++ * minimum supported version for this device), the first entry's
++ * config_set is selected anyway so firmware updates can still be done
++ * (requires only the ACK handler), but the usual mixer controls
++ * aren't created.
++ */
++static void scarlett2_resolve_config_set(struct scarlett2_data *private)
++{
++      const struct scarlett2_config_set_entry *entry =
++              private->info->config_sets;
++
++      private->config_set = entry->config_set;
++      for (entry++; entry->config_set; entry++)
++              if (entry->from_firmware_version <= private->firmware_version)
++                      private->config_set = entry->config_set;
++}
++
+ static void scarlett2_count_io(struct scarlett2_data *private)
+ {
+       const struct scarlett2_device_info *info = private->info;
+-      const struct scarlett2_config_set *config_set = info->config_set;
++      const struct scarlett2_config_set *config_set = private->config_set;
+       const int (*port_count)[SCARLETT2_PORT_DIRNS] = info->port_count;
+       int port_type, srcs = 0, dsts = 0, i;
+@@ -8311,9 +8398,14 @@ static int scarlett2_init_private(struct
+       mixer->private_suspend = scarlett2_private_suspend;
+       private->info = entry->info;
+-      private->config_set = entry->info->config_set;
++
++      /* Set config_set to the first entry's config_set so the
++       * notify handler has a valid pointer while USB init runs; it
++       * is re-resolved once the firmware version has been read.
++       */
++      private->config_set = entry->info->config_sets[0].config_set;
++
+       private->series_name = entry->series_name;
+-      scarlett2_count_io(private);
+       private->scarlett2_seq = 0;
+       private->mixer = mixer;
+@@ -8717,6 +8809,13 @@ static int snd_scarlett2_controls_create
+       if (err < 0)
+               return err;
++      /* Now that the firmware version is known, pick the matching
++       * config_set
++       */
++      scarlett2_resolve_config_set(private);
++
++      scarlett2_count_io(private);
++
+       /* Get the upgrade & settings flash segment numbers */
+       err = scarlett2_get_flash_segment_nums(mixer);
+       if (err < 0)
diff --git a/queue-6.18/alsa-scarlett2-update-offsets-for-2i2-gen-4-firmware-2417.patch b/queue-6.18/alsa-scarlett2-update-offsets-for-2i2-gen-4-firmware-2417.patch
new file mode 100644 (file)
index 0000000..6b6d00a
--- /dev/null
@@ -0,0 +1,103 @@
+From stable+bounces-274083-greg=kroah.com@vger.kernel.org Tue Jul 14 04:09:27 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 22:09:14 -0400
+Subject: ALSA: scarlett2: Update offsets for 2i2 Gen 4 firmware 2417
+To: stable@vger.kernel.org
+Cc: "Geoffrey D. Bennett" <g@b4.vu>, Takashi Iwai <tiwai@suse.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714020914.2356076-2-sashal@kernel.org>
+
+From: "Geoffrey D. Bennett" <g@b4.vu>
+
+[ Upstream commit 3ca15754b561483aa7a1bce51677d6389f8ff5bb ]
+
+Firmware 2417 for the Scarlett 4th Gen 2i2 moved the direct monitor gain
+parameters, so add a second config_set with the shifted offset and
+select it for firmware versions >= 2417.
+
+Fixes: 4e809a299677 ("ALSA: scarlett2: Add support for Solo, 2i2, and 4i4 Gen 4")
+Cc: stable@vger.kernel.org # ALSA: scarlett2: Allow selecting config_set by firmware version
+Cc: stable@vger.kernel.org # ALSA: scarlett2: Fold min_firmware_version into config_sets
+Cc: stable@vger.kernel.org
+Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/ad0fc5a131e76eb656a24e0e198382f7134068fe.1777151532.git.g@b4.vu
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ sound/usb/mixer_scarlett2.c |   58 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 58 insertions(+)
+
+--- a/sound/usb/mixer_scarlett2.c
++++ b/sound/usb/mixer_scarlett2.c
+@@ -937,6 +937,63 @@ static const struct scarlett2_config_set
+       }
+ };
++/* 2i2 Gen 4, firmware version 2417 and above
++ *
++ * Firmware 2417 shifted DIRECT_MONITOR_GAIN by 4 bytes; all other
++ * offsets are unchanged from scarlett2_config_set_gen4_2i2.
++ */
++static const struct scarlett2_config_set scarlett2_config_set_gen4_2i2_2417 = {
++      .notifications = scarlett4_2i2_notifications,
++      .param_buf_addr = 0xfc,
++      .input_gain_tlv = db_scale_gen4_gain,
++      .autogain_status_texts = scarlett2_autogain_status_gen4,
++      .items = {
++              [SCARLETT2_CONFIG_MSD_SWITCH] = {
++                      .offset = 0x49, .size = 8, .activate = 4 },
++
++              [SCARLETT2_CONFIG_DIRECT_MONITOR] = {
++                      .offset = 0x14a, .size = 8, .activate = 16, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_AUTOGAIN_SWITCH] = {
++                      .offset = 0x135, .size = 8, .activate = 10, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_AUTOGAIN_STATUS] = {
++                      .offset = 0x137, .size = 8 },
++
++              [SCARLETT2_CONFIG_AG_MEAN_TARGET] = {
++                      .offset = 0x131, .size = 8, .activate = 29, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_AG_PEAK_TARGET] = {
++                      .offset = 0x132, .size = 8, .activate = 30, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_PHANTOM_SWITCH] = {
++                      .offset = 0x48, .size = 8, .activate = 11, .pbuf = 1,
++                      .mute = 1 },
++
++              [SCARLETT2_CONFIG_INPUT_GAIN] = {
++                      .offset = 0x4b, .size = 8, .activate = 12, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_LEVEL_SWITCH] = {
++                      .offset = 0x3c, .size = 8, .activate = 13, .pbuf = 1,
++                      .mute = 1 },
++
++              [SCARLETT2_CONFIG_SAFE_SWITCH] = {
++                      .offset = 0x147, .size = 8, .activate = 14, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_AIR_SWITCH] = {
++                      .offset = 0x3e, .size = 8, .activate = 15, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_INPUT_SELECT_SWITCH] = {
++                      .offset = 0x14b, .size = 8, .activate = 17, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_INPUT_LINK_SWITCH] = {
++                      .offset = 0x14e, .size = 8, .activate = 18, .pbuf = 1 },
++
++              [SCARLETT2_CONFIG_DIRECT_MONITOR_GAIN] = {
++                      .offset = 0x2a4, .size = 16, .activate = 36 }
++      }
++};
++
+ /* 4i4 Gen 4 */
+ static const struct scarlett2_config_set scarlett2_config_set_gen4_4i4 = {
+       .notifications = scarlett4_4i4_notifications,
+@@ -1999,6 +2056,7 @@ static const struct scarlett2_device_inf
+ static const struct scarlett2_device_info s2i2_gen4_info = {
+       .config_sets = (const struct scarlett2_config_set_entry[]) {
+               { 2115, &scarlett2_config_set_gen4_2i2 },
++              { 2417, &scarlett2_config_set_gen4_2i2_2417 },
+               { }
+       },
+       .min_firmware_version = 2115,
diff --git a/queue-6.18/binder-cache-secctx-size-before-release-zeroes-it.patch b/queue-6.18/binder-cache-secctx-size-before-release-zeroes-it.patch
new file mode 100644 (file)
index 0000000..07e33b2
--- /dev/null
@@ -0,0 +1,105 @@
+From sashal@kernel.org Tue Jul 14 15:43:11 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 09:43:07 -0400
+Subject: binder: cache secctx size before release zeroes it
+To: stable@vger.kernel.org
+Cc: Chris Mason <clm@meta.com>, stable <stable@kernel.org>, Alice Ryhl <aliceryhl@google.com>, Carlos Llamas <cmllamas@google.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714134307.2675829-2-sashal@kernel.org>
+
+From: Chris Mason <clm@meta.com>
+
+[ Upstream commit b34826e55aad3520ec813f1f367c11b24b29dc9f ]
+
+binder_transaction() bounds the scatter-gather buffer area with
+sg_buf_end_offset and subtracts the aligned LSM context size because
+the secctx is written at the tail of that area.  The subtraction reads
+lsmctx.len, but that field has already been cleared by the time the
+line runs:
+
+    security_secid_to_secctx(secid, &lsmctx)   /* lsmctx.len set */
+    lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64))
+    extra_buffers_size += lsmctx_aligned_size
+    ...
+    security_release_secctx(&lsmctx)            /* memset zeroes len */
+    ...
+    sg_buf_end_offset = sg_buf_offset + extra_buffers_size
+                        - ALIGN(lsmctx.len, sizeof(u64)) /* ALIGN(0,8) */
+
+security_release_secctx() does memset(cp, 0, sizeof(*cp)), so lsmctx.len
+reads back as 0 and the subtraction contributes nothing, leaving
+sg_buf_end_offset too large by the aligned secctx size on every
+transaction to a txn_security_ctx node.
+
+Each BINDER_TYPE_PTR object then derives buf_left = sg_buf_end_offset -
+sg_buf_offset as the sole upper bound on its copy, so the inflated end
+offset lets the copy run into the bytes that already hold the secctx.
+
+The aligned size must therefore be cached before release rather than
+re-read from the now-cleared field.  Fix by caching it in
+lsmctx_aligned_size at function scope when it is first computed and
+subtracting lsmctx_aligned_size instead of re-reading lsmctx.len after
+release.  Reuse the same value for the earlier buf_offset computation.
+
+Fixes: 6fba89813ccf ("lsm: ensure the correct LSM context releaser")
+Cc: stable <stable@kernel.org>
+Assisted-by: kres:claude-opus-4-8
+Signed-off-by: Chris Mason <clm@meta.com>
+Reviewed-by: Alice Ryhl <aliceryhl@google.com>
+Acked-by: Carlos Llamas <cmllamas@google.com>
+Link: https://patch.msgid.link/20260603174506.1957278-1-clm@meta.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/android/binder.c |   12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/android/binder.c
++++ b/drivers/android/binder.c
+@@ -3097,6 +3097,7 @@ static void binder_transaction(struct bi
+       int t_debug_id = atomic_inc_return(&binder_last_id);
+       ktime_t t_start_time = ktime_get();
+       struct lsm_context lsmctx = { };
++      size_t lsmctx_aligned_size = 0;
+       LIST_HEAD(sgc_head);
+       LIST_HEAD(pf_head);
+       const void __user *user_buffer = (const void __user *)
+@@ -3363,7 +3364,6 @@ static void binder_transaction(struct bi
+       if (target_node && target_node->txn_security_ctx) {
+               u32 secid;
+-              size_t added_size;
+               security_cred_getsecid(proc->cred, &secid);
+               ret = security_secid_to_secctx(secid, &lsmctx);
+@@ -3375,9 +3375,9 @@ static void binder_transaction(struct bi
+                       return_error_line = __LINE__;
+                       goto err_get_secctx_failed;
+               }
+-              added_size = ALIGN(lsmctx.len, sizeof(u64));
+-              extra_buffers_size += added_size;
+-              if (extra_buffers_size < added_size) {
++              lsmctx_aligned_size = ALIGN(lsmctx.len, sizeof(u64));
++              extra_buffers_size += lsmctx_aligned_size;
++              if (extra_buffers_size < lsmctx_aligned_size) {
+                       binder_txn_error("%d:%d integer overflow of extra_buffers_size\n",
+                               thread->pid, proc->pid);
+                       return_error = BR_FAILED_REPLY;
+@@ -3414,7 +3414,7 @@ static void binder_transaction(struct bi
+               size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) +
+                                   ALIGN(tr->offsets_size, sizeof(void *)) +
+                                   ALIGN(extra_buffers_size, sizeof(void *)) -
+-                                  ALIGN(lsmctx.len, sizeof(u64));
++                                  lsmctx_aligned_size;
+               t->security_ctx = t->buffer->user_data + buf_offset;
+               err = binder_alloc_copy_to_buffer(&target_proc->alloc,
+@@ -3469,7 +3469,7 @@ static void binder_transaction(struct bi
+       off_end_offset = off_start_offset + tr->offsets_size;
+       sg_buf_offset = ALIGN(off_end_offset, sizeof(void *));
+       sg_buf_end_offset = sg_buf_offset + extra_buffers_size -
+-              ALIGN(lsmctx.len, sizeof(u64));
++              lsmctx_aligned_size;
+       off_min = 0;
+       for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
+            buffer_offset += sizeof(binder_size_t)) {
diff --git a/queue-6.18/binder-use-list_head-to-initialize-on-stack-list-head.patch b/queue-6.18/binder-use-list_head-to-initialize-on-stack-list-head.patch
new file mode 100644 (file)
index 0000000..3eac07b
--- /dev/null
@@ -0,0 +1,52 @@
+From sashal@kernel.org Tue Jul 14 15:43:10 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 09:43:06 -0400
+Subject: binder: Use LIST_HEAD() to initialize on stack list head
+To: stable@vger.kernel.org
+Cc: Jisheng Zhang <jszhang@kernel.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714134307.2675829-1-sashal@kernel.org>
+
+From: Jisheng Zhang <jszhang@kernel.org>
+
+[ Upstream commit 91f818b184b44b105b1c92859ea8d2d6f47912a9 ]
+
+Use LIST_HEAD to initialize on stack list head. No intentional
+functional impact.
+
+Change generated with below coccinelle script:
+
+@@
+identifier name;
+@@
+- struct list_head name;
++ LIST_HEAD(name);
+... when != name
+- INIT_LIST_HEAD(&name);
+
+Signed-off-by: Jisheng Zhang <jszhang@kernel.org>
+Link: https://patch.msgid.link/20260519055623.13142-1-jszhang@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: b34826e55aad ("binder: cache secctx size before release zeroes it")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/android/binder.c |    6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/android/binder.c
++++ b/drivers/android/binder.c
+@@ -3097,12 +3097,10 @@ static void binder_transaction(struct bi
+       int t_debug_id = atomic_inc_return(&binder_last_id);
+       ktime_t t_start_time = ktime_get();
+       struct lsm_context lsmctx = { };
+-      struct list_head sgc_head;
+-      struct list_head pf_head;
++      LIST_HEAD(sgc_head);
++      LIST_HEAD(pf_head);
+       const void __user *user_buffer = (const void __user *)
+                               (uintptr_t)tr->data.ptr.buffer;
+-      INIT_LIST_HEAD(&sgc_head);
+-      INIT_LIST_HEAD(&pf_head);
+       e = binder_transaction_log_add(&binder_transaction_log);
+       e->debug_id = t_debug_id;
diff --git a/queue-6.18/bluetooth-6lowpan-fix-cyclic-locking-warning-on-netdev-unregister.patch b/queue-6.18/bluetooth-6lowpan-fix-cyclic-locking-warning-on-netdev-unregister.patch
new file mode 100644 (file)
index 0000000..7af1d52
--- /dev/null
@@ -0,0 +1,101 @@
+From stable+bounces-275041-greg=kroah.com@vger.kernel.org Wed Jul 15 23:13:51 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 17:13:37 -0400
+Subject: Bluetooth: 6lowpan: fix cyclic locking warning on netdev unregister
+To: stable@vger.kernel.org
+Cc: Pauli Virtanen <pav@iki.fi>, Luiz Augusto von Dentz <luiz.von.dentz@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715211338.252572-1-sashal@kernel.org>
+
+From: Pauli Virtanen <pav@iki.fi>
+
+[ Upstream commit 9707a015fe8f3ba8ec7c270f3b2b8efb38823d6b ]
+
+6lowpan.c has theoretically conflicting lock orderings, which lockdep
+complains about:
+
+    a) rtnl_lock > hdev->workqueue
+
+    from 6lowpan.c:delete_netdev -> rtnl_lock -> device_del
+    -> put_device(parent) -> hci_release_dev -> destroy_workqueue
+
+    b) hdev->workqueue > l2cap_conn->lock > chan->lock > rtnl_lock
+
+    from hci_rx_work -> 6lowpan.c:chan_ready_cb
+    -> lowpan_register_netdev, ifup -> rtnl_lock
+
+Actual deadlock appears not possible, as hci_rx_work is disabled and
+l2cap_conn flushed already on hdev unregister. Hence, do minimal thing
+to make lockdep happy by breaking chain a) by holding hdev refcount
+until after netdev put in 6lowpan.c.
+
+Fixes the lockdep complaint:
+WARNING: possible circular locking dependency detected.
+kworker/0:1/11 is trying to acquire lock:
+ffff8880023b3940 ((wq_completion)hci0#2){+.+.}-{0:0}, at: touch_wq_lockdep_map+0x8b/0x130
+but task is already holding lock:
+ffffffff95e4f9c0 (rtnl_mutex){+.+.}-{4:4}, at: lowpan_unregister_netdev+0xd/0x30
+Workqueue: events delete_netdev
+
+Signed-off-by: Pauli Virtanen <pav@iki.fi>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Stable-dep-of: 6fef032af009 ("Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/bluetooth/6lowpan.c |   25 +++++++++++++++++++++++--
+ 1 file changed, 23 insertions(+), 2 deletions(-)
+
+--- a/net/bluetooth/6lowpan.c
++++ b/net/bluetooth/6lowpan.c
+@@ -760,13 +760,33 @@ static inline struct l2cap_chan *chan_ne
+       return chan;
+ }
++static void unregister_dev(struct lowpan_btle_dev *dev)
++{
++      struct hci_dev *hdev = READ_ONCE(dev->hdev);
++
++      /* If netdev holds last reference to hci_dev (its parent device), this
++       * leads to theoretical cyclic locking on lowpan_unregister_netdev:
++       *
++       * rtnl_lock -> put_device(parent) -> hci_release_dev ->
++       * destroy_workqueue -> hci_rx_work -> l2cap_recv_acldata ->
++       * chan_ready_cb -> ifup -> rtnl_lock
++       *
++       * However, hci_rx_work is disabled in hci_unregister_dev, so this
++       * should not occur. Make lockdep happy by postponing hdev release after
++       * netdev put.
++       */
++      hci_dev_hold(hdev);
++      lowpan_unregister_netdev(dev->netdev);
++      hci_dev_put(hdev);
++}
++
+ static void delete_netdev(struct work_struct *work)
+ {
+       struct lowpan_btle_dev *entry = container_of(work,
+                                                    struct lowpan_btle_dev,
+                                                    delete_netdev);
+-      lowpan_unregister_netdev(entry->netdev);
++      unregister_dev(entry);
+       /* The entry pointer is deleted by the netdev destructor. */
+ }
+@@ -1249,6 +1269,7 @@ static void disconnect_devices(void)
+                       break;
+               new_dev->netdev = entry->netdev;
++              new_dev->hdev = entry->hdev;
+               INIT_LIST_HEAD(&new_dev->list);
+               list_add_rcu(&new_dev->list, &devices);
+@@ -1260,7 +1281,7 @@ static void disconnect_devices(void)
+               ifdown(entry->netdev);
+               BT_DBG("Unregistering netdev %s %p",
+                      entry->netdev->name, entry->netdev);
+-              lowpan_unregister_netdev(entry->netdev);
++              unregister_dev(entry);
+               kfree(entry);
+       }
+ }
diff --git a/queue-6.18/bluetooth-l2cap-fix-use-after-free-in-l2cap_sock_new_connection_cb.patch b/queue-6.18/bluetooth-l2cap-fix-use-after-free-in-l2cap_sock_new_connection_cb.patch
new file mode 100644 (file)
index 0000000..85b69ba
--- /dev/null
@@ -0,0 +1,542 @@
+From stable+bounces-275042-greg=kroah.com@vger.kernel.org Wed Jul 15 23:13:47 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 17:13:38 -0400
+Subject: Bluetooth: L2CAP: Fix use-after-free in l2cap_sock_new_connection_cb()
+To: stable@vger.kernel.org
+Cc: Siwei Zhang <oss@fourdim.xyz>, stable@kernel.org, Luiz Augusto von Dentz <luiz.von.dentz@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715211338.252572-2-sashal@kernel.org>
+
+From: Siwei Zhang <oss@fourdim.xyz>
+
+[ Upstream commit 6fef032af0092ed5ccb767239a9ac1bc38c08a40 ]
+
+l2cap_sock_new_connection_cb() returned l2cap_pi(sk)->chan after
+release_sock(parent). Once the parent lock is dropped the newly
+enqueued child socket sk is reachable via the accept queue, so another
+task can accept and free it before the callback dereferences sk,
+resulting in a use-after-free.
+
+Rework the ->new_connection() op so the core, rather than the callback,
+owns the child channel's lifetime. The op now receives a pre-allocated
+new_chan and returns an errno instead of allocating and returning a
+channel. l2cap_new_connection() allocates the child channel and links
+it into the conn list via __l2cap_chan_add() before invoking the
+callback, so the conn-list reference keeps the channel alive once
+release_sock(parent) exposes the socket to other tasks.
+
+Channel configuration that was duplicated in l2cap_sock_init() and the
+various new_connection callbacks is consolidated into
+l2cap_chan_set_defaults(), which now inherits from the parent channel
+when one is supplied.
+
+Fixes: 8ffb929098a5 ("Bluetooth: Remove parent socket usage from l2cap_core.c")
+Cc: stable@kernel.org
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Siwei Zhang <oss@fourdim.xyz>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/bluetooth/l2cap.h |   10 ++--
+ net/bluetooth/6lowpan.c       |   18 -------
+ net/bluetooth/l2cap_core.c    |   78 ++++++++++++++++++++++++++-----
+ net/bluetooth/l2cap_sock.c    |  103 +++++++++++++++++++-----------------------
+ net/bluetooth/smp.c           |   27 ++---------
+ 5 files changed, 127 insertions(+), 109 deletions(-)
+
+--- a/include/net/bluetooth/l2cap.h
++++ b/include/net/bluetooth/l2cap.h
+@@ -620,7 +620,8 @@ struct l2cap_chan {
+ struct l2cap_ops {
+       char                    *name;
+-      struct l2cap_chan       *(*new_connection) (struct l2cap_chan *chan);
++      int                     (*new_connection)(struct l2cap_chan *chan,
++                                                struct l2cap_chan *new_chan);
+       int                     (*recv) (struct l2cap_chan * chan,
+                                        struct sk_buff *skb);
+       void                    (*teardown) (struct l2cap_chan *chan, int err);
+@@ -885,9 +886,10 @@ static inline __u16 __next_seq(struct l2
+       return (seq + 1) % (chan->tx_win_max + 1);
+ }
+-static inline struct l2cap_chan *l2cap_chan_no_new_connection(struct l2cap_chan *chan)
++static inline int l2cap_chan_no_new_connection(struct l2cap_chan *chan,
++                                             struct l2cap_chan *new_chan)
+ {
+-      return NULL;
++      return -EOPNOTSUPP;
+ }
+ static inline int l2cap_chan_no_recv(struct l2cap_chan *chan, struct sk_buff *skb)
+@@ -964,7 +966,7 @@ int l2cap_chan_send(struct l2cap_chan *c
+ void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
+ void l2cap_chan_rx_avail(struct l2cap_chan *chan, ssize_t rx_avail);
+ int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator);
+-void l2cap_chan_set_defaults(struct l2cap_chan *chan);
++void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan);
+ int l2cap_ertm_init(struct l2cap_chan *chan);
+ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
+ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
+--- a/net/bluetooth/6lowpan.c
++++ b/net/bluetooth/6lowpan.c
+@@ -632,7 +632,7 @@ static struct l2cap_chan *chan_create(vo
+       if (!chan)
+               return NULL;
+-      l2cap_chan_set_defaults(chan);
++      l2cap_chan_set_defaults(chan, NULL);
+       chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
+       chan->mode = L2CAP_MODE_LE_FLOWCTL;
+@@ -745,21 +745,6 @@ static inline void chan_ready_cb(struct
+       ifup(dev->netdev);
+ }
+-static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
+-{
+-      struct l2cap_chan *chan;
+-
+-      chan = chan_create();
+-      if (!chan)
+-              return NULL;
+-
+-      chan->ops = pchan->ops;
+-
+-      BT_DBG("chan %p pchan %p", chan, pchan);
+-
+-      return chan;
+-}
+-
+ static void unregister_dev(struct lowpan_btle_dev *dev)
+ {
+       struct hci_dev *hdev = READ_ONCE(dev->hdev);
+@@ -889,7 +874,6 @@ static long chan_get_sndtimeo_cb(struct
+ static const struct l2cap_ops bt_6lowpan_chan_ops = {
+       .name                   = "L2CAP 6LoWPAN channel",
+-      .new_connection         = chan_new_conn_cb,
+       .recv                   = chan_recv_cb,
+       .close                  = chan_close_cb,
+       .state_change           = chan_state_change_cb,
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -525,7 +525,10 @@ void l2cap_chan_put(struct l2cap_chan *c
+ }
+ EXPORT_SYMBOL_GPL(l2cap_chan_put);
+-void l2cap_chan_set_defaults(struct l2cap_chan *chan)
++/* Initialise @chan with default values, inheriting from the parent channel
++ * @pchan when it is given.
++ */
++void l2cap_chan_set_defaults(struct l2cap_chan *chan, struct l2cap_chan *pchan)
+ {
+       chan->fcs  = L2CAP_FCS_CRC16;
+       chan->max_tx = L2CAP_DEFAULT_MAX_TX;
+@@ -539,6 +542,31 @@ void l2cap_chan_set_defaults(struct l2ca
+       chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+       chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
++      if (pchan) {
++              BT_DBG("chan %p pchan %p", chan, pchan);
++
++              chan->chan_type = pchan->chan_type;
++              chan->imtu = pchan->imtu;
++              chan->omtu = pchan->omtu;
++              chan->mode = pchan->mode;
++              chan->fcs = pchan->fcs;
++              chan->max_tx = pchan->max_tx;
++              chan->tx_win = pchan->tx_win;
++              chan->tx_win_max = pchan->tx_win_max;
++              chan->sec_level = pchan->sec_level;
++              chan->conf_state = pchan->conf_state;
++              chan->flags = pchan->flags;
++              chan->tx_credits = pchan->tx_credits;
++              chan->rx_credits = pchan->rx_credits;
++
++              if (chan->chan_type == L2CAP_CHAN_FIXED) {
++                      chan->scid = pchan->scid;
++                      chan->dcid = pchan->scid;
++              }
++
++              return;
++      }
++
+       chan->conf_state = 0;
+       set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
+@@ -4027,6 +4055,38 @@ static inline int l2cap_command_rej(stru
+       return 0;
+ }
++/* Allocate and initialise a channel for an incoming connection.
++ *
++ * The channel inherits its configuration from @pchan and is linked into @conn
++ * before ->new_connection() runs, so the conn list reference keeps it alive if
++ * the callback exposes it (e.g. via the socket accept queue) before this
++ * returns. The l2cap_chan_create() reference is taken over by the subsystem on
++ * success and dropped here on failure.
++ */
++static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
++                                             struct l2cap_chan *pchan)
++{
++      struct l2cap_chan *chan;
++
++      chan = l2cap_chan_create();
++      if (!chan)
++              return NULL;
++
++      l2cap_chan_set_defaults(chan, pchan);
++      chan->ops = pchan->ops;
++
++      __l2cap_chan_add(conn, chan);
++
++      if (pchan->ops->new_connection &&
++          pchan->ops->new_connection(pchan, chan) < 0) {
++              l2cap_chan_del(chan, 0);
++              l2cap_chan_put(chan);
++              return NULL;
++      }
++
++      return chan;
++}
++
+ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd,
+                         u8 *data, u8 rsp_code)
+ {
+@@ -4073,7 +4133,7 @@ static void l2cap_connect(struct l2cap_c
+               goto response;
+       }
+-      chan = pchan->ops->new_connection(pchan);
++      chan = l2cap_new_connection(conn, pchan);
+       if (!chan)
+               goto response;
+@@ -4091,8 +4151,6 @@ static void l2cap_connect(struct l2cap_c
+       chan->psm  = psm;
+       chan->dcid = scid;
+-      __l2cap_chan_add(conn, chan);
+-
+       dcid = chan->scid;
+       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
+@@ -4984,7 +5042,7 @@ static int l2cap_le_connect_req(struct l
+               goto response_unlock;
+       }
+-      chan = pchan->ops->new_connection(pchan);
++      chan = l2cap_new_connection(conn, pchan);
+       if (!chan) {
+               result = L2CAP_CR_LE_NO_MEM;
+               goto response_unlock;
+@@ -4999,8 +5057,6 @@ static int l2cap_le_connect_req(struct l
+       chan->omtu = mtu;
+       chan->remote_mps = mps;
+-      __l2cap_chan_add(conn, chan);
+-
+       l2cap_le_flowctl_init(chan, __le16_to_cpu(req->credits));
+       dcid = chan->scid;
+@@ -5208,7 +5264,7 @@ static inline int l2cap_ecred_conn_req(s
+                       continue;
+               }
+-              chan = pchan->ops->new_connection(pchan);
++              chan = l2cap_new_connection(conn, pchan);
+               if (!chan) {
+                       result = L2CAP_CR_LE_NO_MEM;
+                       continue;
+@@ -5223,8 +5279,6 @@ static inline int l2cap_ecred_conn_req(s
+               chan->omtu = mtu;
+               chan->remote_mps = mps;
+-              __l2cap_chan_add(conn, chan);
+-
+               l2cap_ecred_init(chan, __le16_to_cpu(req->credits));
+               /* Init response */
+@@ -7502,14 +7556,12 @@ static void l2cap_connect_cfm(struct hci
+                       goto next;
+               l2cap_chan_lock(pchan);
+-              chan = pchan->ops->new_connection(pchan);
++              chan = l2cap_new_connection(conn, pchan);
+               if (chan) {
+                       bacpy(&chan->src, &hcon->src);
+                       bacpy(&chan->dst, &hcon->dst);
+                       chan->src_type = bdaddr_src_type(hcon);
+                       chan->dst_type = dst_type;
+-
+-                      __l2cap_chan_add(conn, chan);
+               }
+               l2cap_chan_unlock(pchan);
+--- a/net/bluetooth/l2cap_sock.c
++++ b/net/bluetooth/l2cap_sock.c
+@@ -45,7 +45,8 @@ static struct bt_sock_list l2cap_sk_list
+ static const struct proto_ops l2cap_sock_ops;
+ static void l2cap_sock_init(struct sock *sk, struct sock *parent);
+ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
+-                                   int proto, gfp_t prio, int kern);
++                                   int proto, gfp_t prio, int kern,
++                                   struct l2cap_chan *chan);
+ static void l2cap_sock_cleanup_listen(struct sock *parent);
+ bool l2cap_is_socket(struct socket *sock)
+@@ -1256,6 +1257,23 @@ done:
+       return err;
+ }
++/* Release the sock's ref on chan and clear the pointer so that the ref is
++ * dropped exactly once even if both l2cap_sock_kill() and
++ * l2cap_sock_destruct() run. Setting chan->data to NULL first stops any other
++ * task from dereferencing the now-dead sock pointer.
++ */
++static void l2cap_sock_put_chan(struct sock *sk)
++{
++      struct l2cap_chan *chan = l2cap_pi(sk)->chan;
++
++      if (!chan)
++              return;
++
++      chan->data = NULL;
++      l2cap_pi(sk)->chan = NULL;
++      l2cap_chan_put(chan);
++}
++
+ /* Kill socket (only if zapped and orphan)
+  * Must be called on unlocked socket, with l2cap channel lock.
+  */
+@@ -1266,13 +1284,9 @@ static void l2cap_sock_kill(struct sock
+       BT_DBG("sk %p state %s", sk, state_to_string(sk->sk_state));
+-      /* Sock is dead, so set chan data to NULL, avoid other task use invalid
+-       * sock pointer.
+-       */
+-      l2cap_pi(sk)->chan->data = NULL;
+-      /* Kill poor orphan */
++      l2cap_sock_put_chan(sk);
+-      l2cap_chan_put(l2cap_pi(sk)->chan);
++      /* Kill poor orphan */
+       sock_set_flag(sk, SOCK_DEAD);
+       sock_put(sk);
+ }
+@@ -1515,12 +1529,13 @@ static void l2cap_sock_cleanup_listen(st
+       }
+ }
+-static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
++static int l2cap_sock_new_connection_cb(struct l2cap_chan *chan,
++                                      struct l2cap_chan *new_chan)
+ {
+       struct sock *sk, *parent = chan->data;
+       if (!parent)
+-              return NULL;
++              return -EINVAL;
+       lock_sock(parent);
+@@ -1528,25 +1543,28 @@ static struct l2cap_chan *l2cap_sock_new
+       if (sk_acceptq_is_full(parent)) {
+               BT_DBG("backlog full %d", parent->sk_ack_backlog);
+               release_sock(parent);
+-              return NULL;
++              return -ENOBUFS;
+       }
+       sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
+-                            GFP_ATOMIC, 0);
++                            GFP_ATOMIC, 0, new_chan);
+       if (!sk) {
+               release_sock(parent);
+-              return NULL;
+-        }
++              return -ENOMEM;
++      }
+       bt_sock_reclassify_lock(sk, BTPROTO_L2CAP);
+       l2cap_sock_init(sk, parent);
++      /* The conn list reference taken by l2cap_new_connection() keeps new_chan
++       * alive once release_sock() lets another task free this socket.
++       */
+       bt_accept_enqueue(parent, sk, false);
+       release_sock(parent);
+-      return l2cap_pi(sk)->chan;
++      return 0;
+ }
+ static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
+@@ -1844,10 +1862,7 @@ static void l2cap_sock_destruct(struct s
+       BT_DBG("sk %p", sk);
+-      if (l2cap_pi(sk)->chan) {
+-              l2cap_pi(sk)->chan->data = NULL;
+-              l2cap_chan_put(l2cap_pi(sk)->chan);
+-      }
++      l2cap_sock_put_chan(sk);
+       list_for_each_entry_safe(rx_busy, next, &l2cap_pi(sk)->rx_busy, list) {
+               kfree_skb(rx_busy->skb);
+@@ -1880,30 +1895,12 @@ static void l2cap_sock_init(struct sock
+       BT_DBG("sk %p", sk);
+       if (parent) {
+-              struct l2cap_chan *pchan = l2cap_pi(parent)->chan;
+-
+               sk->sk_type = parent->sk_type;
+               bt_sk(sk)->flags = bt_sk(parent)->flags;
+-              chan->chan_type = pchan->chan_type;
+-              chan->imtu = pchan->imtu;
+-              chan->omtu = pchan->omtu;
+-              chan->conf_state = pchan->conf_state;
+-              chan->mode = pchan->mode;
+-              chan->fcs  = pchan->fcs;
+-              chan->max_tx = pchan->max_tx;
+-              chan->tx_win = pchan->tx_win;
+-              chan->tx_win_max = pchan->tx_win_max;
+-              chan->sec_level = pchan->sec_level;
+-              chan->flags = pchan->flags;
+-              chan->tx_credits = pchan->tx_credits;
+-              chan->rx_credits = pchan->rx_credits;
+-
+-              if (chan->chan_type == L2CAP_CHAN_FIXED) {
+-                      chan->scid = pchan->scid;
+-                      chan->dcid = pchan->scid;
+-              }
+-
++              /* Channel configuration is inherited from the parent by
++               * l2cap_new_connection().
++               */
+               security_sk_clone(parent, sk);
+       } else {
+               switch (sk->sk_type) {
+@@ -1929,7 +1926,7 @@ static void l2cap_sock_init(struct sock
+                       chan->mode = L2CAP_MODE_BASIC;
+               }
+-              l2cap_chan_set_defaults(chan);
++              l2cap_chan_set_defaults(chan, NULL);
+       }
+       /* Default config options */
+@@ -1948,10 +1945,10 @@ static struct proto l2cap_proto = {
+ };
+ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
+-                                   int proto, gfp_t prio, int kern)
++                                   int proto, gfp_t prio, int kern,
++                                   struct l2cap_chan *chan)
+ {
+       struct sock *sk;
+-      struct l2cap_chan *chan;
+       sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern);
+       if (!sk)
+@@ -1962,16 +1959,7 @@ static struct sock *l2cap_sock_alloc(str
+       INIT_LIST_HEAD(&l2cap_pi(sk)->rx_busy);
+-      chan = l2cap_chan_create();
+-      if (!chan) {
+-              sk_free(sk);
+-              if (sock)
+-                      sock->sk = NULL;
+-              return NULL;
+-      }
+-
+-      l2cap_chan_hold(chan);
+-
++      /* The sock takes ownership of the caller's reference on chan. */
+       l2cap_pi(sk)->chan = chan;
+       return sk;
+@@ -1981,6 +1969,7 @@ static int l2cap_sock_create(struct net
+                            int kern)
+ {
+       struct sock *sk;
++      struct l2cap_chan *chan;
+       BT_DBG("sock %p", sock);
+@@ -1995,10 +1984,16 @@ static int l2cap_sock_create(struct net
+       sock->ops = &l2cap_sock_ops;
+-      sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern);
+-      if (!sk)
++      chan = l2cap_chan_create();
++      if (!chan)
+               return -ENOMEM;
++      sk = l2cap_sock_alloc(net, sock, protocol, GFP_ATOMIC, kern, chan);
++      if (!sk) {
++              l2cap_chan_put(chan);
++              return -ENOMEM;
++      }
++
+       l2cap_sock_init(sk, NULL);
+       bt_sock_link(&l2cap_sk_list, sk);
+       return 0;
+--- a/net/bluetooth/smp.c
++++ b/net/bluetooth/smp.c
+@@ -3234,34 +3234,19 @@ static const struct l2cap_ops smp_chan_o
+       .get_sndtimeo           = l2cap_chan_no_get_sndtimeo,
+ };
+-static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
++static inline int smp_new_conn_cb(struct l2cap_chan *chan,
++                                struct l2cap_chan *new_chan)
+ {
+-      struct l2cap_chan *chan;
+-
+-      BT_DBG("pchan %p", pchan);
+-
+-      chan = l2cap_chan_create();
+-      if (!chan)
+-              return NULL;
+-
+-      chan->chan_type = pchan->chan_type;
+-      chan->ops       = &smp_chan_ops;
+-      chan->scid      = pchan->scid;
+-      chan->dcid      = chan->scid;
+-      chan->imtu      = pchan->imtu;
+-      chan->omtu      = pchan->omtu;
+-      chan->mode      = pchan->mode;
++      new_chan->ops = &smp_chan_ops;
+       /* Other L2CAP channels may request SMP routines in order to
+        * change the security level. This means that the SMP channel
+        * lock must be considered in its own category to avoid lockdep
+        * warnings.
+        */
+-      atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
+-
+-      BT_DBG("created chan %p", chan);
++      atomic_set(&new_chan->nesting, L2CAP_NESTING_SMP);
+-      return chan;
++      return 0;
+ }
+ static const struct l2cap_ops smp_root_chan_ops = {
+@@ -3332,7 +3317,7 @@ create_chan:
+       l2cap_add_scid(chan, cid);
+-      l2cap_chan_set_defaults(chan);
++      l2cap_chan_set_defaults(chan, NULL);
+       if (cid == L2CAP_CID_SMP) {
+               u8 bdaddr_type;
diff --git a/queue-6.18/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch b/queue-6.18/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch
new file mode 100644 (file)
index 0000000..9f4bf10
--- /dev/null
@@ -0,0 +1,118 @@
+From stable+bounces-277302-greg=kroah.com@vger.kernel.org Sat Jul 18 13:45:21 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 07:44:41 -0400
+Subject: bpf: Allow LPM map access from sleepable BPF programs
+To: stable@vger.kernel.org
+Cc: Vlad Poenaru <vlad.wing@gmail.com>, Emil Tsalapatis <emil@etsalapatis.com>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718114441.3048017-2-sashal@kernel.org>
+
+From: Vlad Poenaru <vlad.wing@gmail.com>
+
+[ Upstream commit 2f884d371fafea137afea504d49ee4a7c8d7985b ]
+
+trie_lookup_elem() annotates its rcu_dereference_check() walks with
+only rcu_read_lock_bh_held().  Because rcu_dereference_check(p, c)
+resolves to "c || rcu_read_lock_held()", this passes for XDP/NAPI and
+classic RCU readers but fails for sleepable BPF programs, which enter
+via __bpf_prog_enter_sleepable() and hold only rcu_read_lock_trace().
+
+trie_update_elem() and trie_delete_elem() have the same problem in a
+different form: they walk the trie with plain rcu_dereference(), which
+asserts rcu_read_lock_held() unconditionally.  Both are reachable from
+sleepable BPF programs via the bpf_map_update_elem / bpf_map_delete_elem
+helpers, and from the syscall path under classic rcu_read_lock().  In
+the writer paths the trie is actually protected by trie->lock (an
+rqspinlock taken across the walk); we never relied on the RCU read-side
+lock to keep nodes alive there.
+
+A sleepable LSM hook that ends up touching an LPM trie therefore
+triggers lockdep on debug kernels:
+
+  =============================
+  WARNING: suspicious RCU usage
+  7.1.0-... Tainted: G            E
+  -----------------------------
+  kernel/bpf/lpm_trie.c:249 suspicious rcu_dereference_check() usage!
+  1 lock held by net_tests/540:
+   #0: (rcu_tasks_trace_srcu_struct){....}-{0:0},
+       at: __bpf_prog_enter_sleepable+0x26/0x280
+  Call Trace:
+   dump_stack_lvl
+   lockdep_rcu_suspicious
+   trie_lookup_elem
+   bpf_prog_..._enforce_security_socket_connect
+   bpf_trampoline_...
+   security_socket_connect
+   __sys_connect
+   do_syscall_64
+
+This is lockdep-only -- no UAF, since Tasks Trace RCU does serialize
+against the trie's reclaim path -- but it spams the console once per
+distinct callsite on every debug kernel running a sleepable BPF LSM
+that touches an LPM trie, which is increasingly common.
+
+For the lookup path, switch the rcu_dereference_check() annotation
+from rcu_read_lock_bh_held() to bpf_rcu_lock_held(), which accepts all
+three contexts (classic, BH, Tasks Trace).  Other map types already
+follow this convention.
+
+For trie_update_elem() and trie_delete_elem(), annotate the walks as
+rcu_dereference_protected(*p, 1) -- matching trie_free() in the same
+file -- since trie->lock is held across the walk.  rqspinlock has no
+lockdep_map, so the predicate degenerates to '1' rather than
+lockdep_is_held(&trie->lock); the protection is real but not
+machine-verifiable.  trie_get_next_key() also uses bare
+rcu_dereference() but is reachable only from the BPF syscall, which
+holds classic rcu_read_lock() before dispatching, so it is left
+untouched.
+
+Fixes: 694cea395fde ("bpf: Allow RCU-protected lookups to happen from bh context")
+Cc: stable@vger.kernel.org
+Signed-off-by: Vlad Poenaru <vlad.wing@gmail.com>
+Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>
+Link: https://lore.kernel.org/r/20260609135558.193287-2-vlad.wing@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/bpf/lpm_trie.c |    8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/kernel/bpf/lpm_trie.c
++++ b/kernel/bpf/lpm_trie.c
+@@ -246,7 +246,7 @@ static void *trie_lookup_elem(struct bpf
+       /* Start walking the trie from the root node ... */
+-      for (node = rcu_dereference_check(trie->root, rcu_read_lock_bh_held());
++      for (node = rcu_dereference_check(trie->root, bpf_rcu_lock_held());
+            node;) {
+               unsigned int next_bit;
+               size_t matchlen;
+@@ -280,7 +280,7 @@ static void *trie_lookup_elem(struct bpf
+                */
+               next_bit = extract_bit(key->data, node->prefixlen);
+               node = rcu_dereference_check(node->child[next_bit],
+-                                           rcu_read_lock_bh_held());
++                                           bpf_rcu_lock_held());
+       }
+       if (!found)
+@@ -359,7 +359,7 @@ static long trie_update_elem(struct bpf_
+        */
+       slot = &trie->root;
+-      while ((node = rcu_dereference(*slot))) {
++      while ((node = rcu_dereference_protected(*slot, 1))) {
+               matchlen = longest_prefix_match(trie, node, key);
+               if (node->prefixlen != matchlen ||
+@@ -482,7 +482,7 @@ static long trie_delete_elem(struct bpf_
+       trim = &trie->root;
+       trim2 = trim;
+       parent = NULL;
+-      while ((node = rcu_dereference(*trim))) {
++      while ((node = rcu_dereference_protected(*trim, 1))) {
+               matchlen = longest_prefix_match(trie, node, key);
+               if (node->prefixlen != matchlen ||
diff --git a/queue-6.18/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch b/queue-6.18/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch
new file mode 100644 (file)
index 0000000..7b93711
--- /dev/null
@@ -0,0 +1,168 @@
+From stable+bounces-277301-greg=kroah.com@vger.kernel.org Sat Jul 18 13:45:16 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 07:44:40 -0400
+Subject: bpf: Consistently use bpf_rcu_lock_held() everywhere
+To: stable@vger.kernel.org
+Cc: Andrii Nakryiko <andrii@kernel.org>, Daniel Borkmann <daniel@iogearbox.net>, Jiri Olsa <jolsa@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718114441.3048017-1-sashal@kernel.org>
+
+From: Andrii Nakryiko <andrii@kernel.org>
+
+[ Upstream commit 48a97ffc6c826640907d13b199e29008f4fe2c15 ]
+
+We have many places which open-code what's now is bpf_rcu_lock_held()
+macro, so replace all those places with a clean and short macro invocation.
+For that, move bpf_rcu_lock_held() macro into include/linux/bpf.h.
+
+Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: Jiri Olsa <jolsa@kernel.org>
+Link: https://lore.kernel.org/bpf/20251014201403.4104511-1-andrii@kernel.org
+Stable-dep-of: 2f884d371faf ("bpf: Allow LPM map access from sleepable BPF programs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/bpf.h               |    3 +++
+ include/linux/bpf_local_storage.h |    3 ---
+ kernel/bpf/hashtab.c              |   21 +++++++--------------
+ kernel/bpf/helpers.c              |   12 ++++--------
+ 4 files changed, 14 insertions(+), 25 deletions(-)
+
+--- a/include/linux/bpf.h
++++ b/include/linux/bpf.h
+@@ -2375,6 +2375,9 @@ bpf_prog_run_array_uprobe(const struct b
+ bool bpf_jit_bypass_spec_v1(void);
+ bool bpf_jit_bypass_spec_v4(void);
++#define bpf_rcu_lock_held() \
++      (rcu_read_lock_held() || rcu_read_lock_trace_held() || rcu_read_lock_bh_held())
++
+ #ifdef CONFIG_BPF_SYSCALL
+ DECLARE_PER_CPU(int, bpf_prog_active);
+ extern struct mutex bpf_stats_enabled_mutex;
+--- a/include/linux/bpf_local_storage.h
++++ b/include/linux/bpf_local_storage.h
+@@ -18,9 +18,6 @@
+ #define BPF_LOCAL_STORAGE_CACHE_SIZE  16
+-#define bpf_rcu_lock_held()                                                    \
+-      (rcu_read_lock_held() || rcu_read_lock_trace_held() ||                 \
+-       rcu_read_lock_bh_held())
+ struct bpf_local_storage_map_bucket {
+       struct hlist_head list;
+       raw_spinlock_t lock;
+--- a/kernel/bpf/hashtab.c
++++ b/kernel/bpf/hashtab.c
+@@ -669,8 +669,7 @@ static void *__htab_map_lookup_elem(stru
+       struct htab_elem *l;
+       u32 hash, key_size;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1104,8 +1103,7 @@ static long htab_map_update_elem(struct
+               /* unknown flags */
+               return -EINVAL;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1212,8 +1210,7 @@ static long htab_lru_map_update_elem(str
+               /* unknown flags */
+               return -EINVAL;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1281,8 +1278,7 @@ static long htab_map_update_elem_in_plac
+               /* unknown flags */
+               return -EINVAL;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1344,8 +1340,7 @@ static long __htab_lru_percpu_map_update
+               /* unknown flags */
+               return -EINVAL;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1422,8 +1417,7 @@ static long htab_map_delete_elem(struct
+       u32 hash, key_size;
+       int ret;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+@@ -1458,8 +1452,7 @@ static long htab_lru_map_delete_elem(str
+       u32 hash, key_size;
+       int ret;
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       key_size = map->key_size;
+--- a/kernel/bpf/helpers.c
++++ b/kernel/bpf/helpers.c
+@@ -42,8 +42,7 @@
+  */
+ BPF_CALL_2(bpf_map_lookup_elem, struct bpf_map *, map, void *, key)
+ {
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       return (unsigned long) map->ops->map_lookup_elem(map, key);
+ }
+@@ -59,8 +58,7 @@ const struct bpf_func_proto bpf_map_look
+ BPF_CALL_4(bpf_map_update_elem, struct bpf_map *, map, void *, key,
+          void *, value, u64, flags)
+ {
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       return map->ops->map_update_elem(map, key, value, flags);
+ }
+@@ -77,8 +75,7 @@ const struct bpf_func_proto bpf_map_upda
+ BPF_CALL_2(bpf_map_delete_elem, struct bpf_map *, map, void *, key)
+ {
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       return map->ops->map_delete_elem(map, key);
+ }
+@@ -134,8 +131,7 @@ const struct bpf_func_proto bpf_map_peek
+ BPF_CALL_3(bpf_map_lookup_percpu_elem, struct bpf_map *, map, void *, key, u32, cpu)
+ {
+-      WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held() &&
+-                   !rcu_read_lock_bh_held());
++      WARN_ON_ONCE(!bpf_rcu_lock_held());
+       return (unsigned long) map->ops->map_lookup_percpu_elem(map, key, cpu);
+ }
diff --git a/queue-6.18/bpf-consistently-use-reg_state-for-register-access-in-the-verifier.patch b/queue-6.18/bpf-consistently-use-reg_state-for-register-access-in-the-verifier.patch
new file mode 100644 (file)
index 0000000..6f4d2d2
--- /dev/null
@@ -0,0 +1,198 @@
+From stable+bounces-277298-greg=kroah.com@vger.kernel.org Sat Jul 18 13:44:45 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 07:44:32 -0400
+Subject: bpf: Consistently use reg_state() for register access in the verifier
+To: stable@vger.kernel.org
+Cc: Mykyta Yatsenko <yatsenko@meta.com>, Andrii Nakryiko <andrii@kernel.org>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718114435.3046796-1-sashal@kernel.org>
+
+From: Mykyta Yatsenko <yatsenko@meta.com>
+
+[ Upstream commit 7af3339948601e188d93d7e03326aeb8fcbd6bea ]
+
+Replace the pattern of declaring a local regs array from cur_regs()
+and then indexing into it with the more concise reg_state() helper.
+This simplifies the code by eliminating intermediate variables and
+makes register access more consistent throughout the verifier.
+
+Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
+Acked-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20260113134826.2214860-1-mykyta.yatsenko5@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Stable-dep-of: 53040a81ae57 ("bpf: Keep dynamic inner array lookups nullable")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/bpf/verifier.c |   42 +++++++++++++++++++-----------------------
+ 1 file changed, 19 insertions(+), 23 deletions(-)
+
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -5690,8 +5690,8 @@ static int check_stack_write(struct bpf_
+ static int check_map_access_type(struct bpf_verifier_env *env, u32 regno,
+                                int off, int size, enum bpf_access_type type)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env);
+-      struct bpf_map *map = regs[regno].map_ptr;
++      struct bpf_reg_state *reg = reg_state(env, regno);
++      struct bpf_map *map = reg->map_ptr;
+       u32 cap = bpf_map_flags_to_cap(map);
+       if (type == BPF_WRITE && !(cap & BPF_MAP_CAN_WRITE)) {
+@@ -6195,8 +6195,7 @@ static bool may_access_direct_pkt_data(s
+ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
+                              int size, bool zero_size_allowed)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env);
+-      struct bpf_reg_state *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       int err;
+       /* We may have added a variable offset to the packet pointer; but any
+@@ -6283,8 +6282,7 @@ static int check_sock_access(struct bpf_
+                            u32 regno, int off, int size,
+                            enum bpf_access_type t)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env);
+-      struct bpf_reg_state *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       struct bpf_insn_access_aux info = {};
+       bool valid;
+@@ -7469,8 +7467,7 @@ static int check_stack_access_within_bou
+               int regno, int off, int access_size,
+               enum bpf_access_type type)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env);
+-      struct bpf_reg_state *reg = regs + regno;
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       struct bpf_func_state *state = func(env, reg);
+       s64 min_off, max_off;
+       int err;
+@@ -8414,7 +8411,7 @@ static int process_spin_lock(struct bpf_
+ {
+       bool is_lock = flags & PROCESS_SPIN_LOCK, is_res_lock = flags & PROCESS_RES_LOCK;
+       const char *lock_str = is_res_lock ? "bpf_res_spin" : "bpf_spin";
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       struct bpf_verifier_state *cur = env->cur_state;
+       bool is_const = tnum_is_const(reg->var_off);
+       bool is_irq = flags & PROCESS_LOCK_IRQ;
+@@ -8530,7 +8527,7 @@ static int process_spin_lock(struct bpf_
+ static int check_map_field_pointer(struct bpf_verifier_env *env, u32 regno,
+                                  enum btf_field_type field_type)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       bool is_const = tnum_is_const(reg->var_off);
+       struct bpf_map *map = reg->map_ptr;
+       u64 val = reg->var_off.value;
+@@ -8577,7 +8574,7 @@ static int check_map_field_pointer(struc
+ static int process_timer_func(struct bpf_verifier_env *env, int regno,
+                             struct bpf_call_arg_meta *meta)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       struct bpf_map *map = reg->map_ptr;
+       int err;
+@@ -8601,7 +8598,7 @@ static int process_timer_func(struct bpf
+ static int process_wq_func(struct bpf_verifier_env *env, int regno,
+                          struct bpf_kfunc_call_arg_meta *meta)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       struct bpf_map *map = reg->map_ptr;
+       int err;
+@@ -8622,7 +8619,7 @@ static int process_wq_func(struct bpf_ve
+ static int process_task_work_func(struct bpf_verifier_env *env, int regno,
+                                 struct bpf_kfunc_call_arg_meta *meta)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       struct bpf_map *map = reg->map_ptr;
+       int err;
+@@ -8642,7 +8639,7 @@ static int process_task_work_func(struct
+ static int process_kptr_func(struct bpf_verifier_env *env, int regno,
+                            struct bpf_call_arg_meta *meta)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       struct btf_field *kptr_field;
+       struct bpf_map *map_ptr;
+       struct btf_record *rec;
+@@ -8715,7 +8712,7 @@ static int process_kptr_func(struct bpf_
+ static int process_dynptr_func(struct bpf_verifier_env *env, int regno, int insn_idx,
+                              enum bpf_arg_type arg_type, int clone_ref_obj_id)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       int err;
+       if (reg->type != PTR_TO_STACK && reg->type != CONST_PTR_TO_DYNPTR) {
+@@ -8835,7 +8832,7 @@ static bool is_kfunc_arg_iter(struct bpf
+ static int process_iter_arg(struct bpf_verifier_env *env, int regno, int insn_idx,
+                           struct bpf_kfunc_call_arg_meta *meta)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       const struct btf_type *t;
+       int spi, err, i, nr_slots, btf_id;
+@@ -9307,7 +9304,7 @@ static int check_reg_type(struct bpf_ver
+                         const u32 *arg_btf_id,
+                         struct bpf_call_arg_meta *meta)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       enum bpf_reg_type expected, type = reg->type;
+       const struct bpf_reg_types *compatible;
+       int i, j;
+@@ -9720,7 +9717,7 @@ static int check_func_arg(struct bpf_ver
+                         int insn_idx)
+ {
+       u32 regno = BPF_REG_1 + arg;
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       enum bpf_arg_type arg_type = fn->arg_type[arg];
+       enum bpf_reg_type type = reg->type;
+       u32 *arg_btf_id = NULL;
+@@ -11250,7 +11247,7 @@ record_func_key(struct bpf_verifier_env
+               int func_id, int insn_idx)
+ {
+       struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
+-      struct bpf_reg_state *regs = cur_regs(env), *reg;
++      struct bpf_reg_state *reg;
+       struct bpf_map *map = meta->map_ptr;
+       u64 val, max;
+       int err;
+@@ -11262,7 +11259,7 @@ record_func_key(struct bpf_verifier_env
+               return -EINVAL;
+       }
+-      reg = &regs[BPF_REG_3];
++      reg = reg_state(env, BPF_REG_3);
+       val = reg->var_off.value;
+       max = map->max_entries;
+@@ -11408,8 +11405,7 @@ static struct bpf_insn_aux_data *cur_aux
+ static bool loop_flag_is_zero(struct bpf_verifier_env *env)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env);
+-      struct bpf_reg_state *reg = &regs[BPF_REG_4];
++      struct bpf_reg_state *reg = reg_state(env, BPF_REG_4);
+       bool reg_is_null = register_is_null(reg);
+       if (reg_is_null)
+@@ -12680,7 +12676,7 @@ static int process_kf_arg_ptr_to_btf_id(
+ static int process_irq_flag(struct bpf_verifier_env *env, int regno,
+                            struct bpf_kfunc_call_arg_meta *meta)
+ {
+-      struct bpf_reg_state *regs = cur_regs(env), *reg = &regs[regno];
++      struct bpf_reg_state *reg = reg_state(env, regno);
+       int err, kfunc_class = IRQ_NATIVE_KFUNC;
+       bool irq_save;
diff --git a/queue-6.18/bpf-introduce-struct-bpf_map_desc-in-verifier.patch b/queue-6.18/bpf-introduce-struct-bpf_map_desc-in-verifier.patch
new file mode 100644 (file)
index 0000000..9aabb5e
--- /dev/null
@@ -0,0 +1,275 @@
+From stable+bounces-277299-greg=kroah.com@vger.kernel.org Sat Jul 18 13:44:45 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 07:44:33 -0400
+Subject: bpf: Introduce struct bpf_map_desc in verifier
+To: stable@vger.kernel.org
+Cc: Mykyta Yatsenko <yatsenko@meta.com>, Eduard Zingerman <eddyz87@gmail.com>, Alexei Starovoitov <ast@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718114435.3046796-2-sashal@kernel.org>
+
+From: Mykyta Yatsenko <yatsenko@meta.com>
+
+[ Upstream commit 98c4fd2963cb2bc5eae22b5365e6bbf3a5cd0f14 ]
+
+Introduce struct bpf_map_desc to hold bpf_map pointer and map uid. Use
+this struct in both bpf_call_arg_meta and bpf_kfunc_call_arg_meta
+instead of having different representations:
+ - bpf_call_arg_meta had separate map_ptr and map_uid fields
+ - bpf_kfunc_call_arg_meta had an anonymous inline struct
+
+This unifies the map fields layout across both metadata structures,
+making the code more consistent and preparing for further refactoring of
+map field pointer validation.
+
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
+Link: https://lore.kernel.org/r/20260130-verif_special_fields-v2-1-2c59e637da7d@meta.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Stable-dep-of: 53040a81ae57 ("bpf: Keep dynamic inner array lookups nullable")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/bpf/verifier.c |   79 +++++++++++++++++++++++++-------------------------
+ 1 file changed, 40 insertions(+), 39 deletions(-)
+
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -274,8 +274,13 @@ static bool bpf_pseudo_kfunc_call(const
+              insn->src_reg == BPF_PSEUDO_KFUNC_CALL;
+ }
++struct bpf_map_desc {
++      struct bpf_map *ptr;
++      int uid;
++};
++
+ struct bpf_call_arg_meta {
+-      struct bpf_map *map_ptr;
++      struct bpf_map_desc map;
+       bool raw_mode;
+       bool pkt_access;
+       u8 release_regno;
+@@ -285,7 +290,6 @@ struct bpf_call_arg_meta {
+       u64 msize_max_value;
+       int ref_obj_id;
+       int dynptr_id;
+-      int map_uid;
+       int func_id;
+       struct btf *btf;
+       u32 btf_id;
+@@ -345,10 +349,7 @@ struct bpf_kfunc_call_arg_meta {
+               u8 spi;
+               u8 frameno;
+       } iter;
+-      struct {
+-              struct bpf_map *ptr;
+-              int uid;
+-      } map;
++      struct bpf_map_desc map;
+       u64 mem_size;
+ };
+@@ -8582,7 +8583,7 @@ static int process_timer_func(struct bpf
+       if (err)
+               return err;
+-      if (meta->map_ptr) {
++      if (meta->map.ptr) {
+               verifier_bug(env, "Two map pointers in a timer helper");
+               return -EFAULT;
+       }
+@@ -8590,8 +8591,8 @@ static int process_timer_func(struct bpf
+               verbose(env, "bpf_timer cannot be used for PREEMPT_RT.\n");
+               return -EOPNOTSUPP;
+       }
+-      meta->map_uid = reg->map_uid;
+-      meta->map_ptr = map;
++      meta->map.uid = reg->map_uid;
++      meta->map.ptr = map;
+       return 0;
+ }
+@@ -8655,7 +8656,7 @@ static int process_kptr_func(struct bpf_
+                       return -EINVAL;
+               }
+               rec = map_ptr->record;
+-              meta->map_ptr = map_ptr;
++              meta->map.ptr = map_ptr;
+       }
+       if (!tnum_is_const(reg->var_off)) {
+@@ -9162,13 +9163,13 @@ static int resolve_map_arg_type(struct b
+                                const struct bpf_call_arg_meta *meta,
+                                enum bpf_arg_type *arg_type)
+ {
+-      if (!meta->map_ptr) {
++      if (!meta->map.ptr) {
+               /* kernel subsystem misconfigured verifier */
+               verifier_bug(env, "invalid map_ptr to access map->type");
+               return -EFAULT;
+       }
+-      switch (meta->map_ptr->map_type) {
++      switch (meta->map.ptr->map_type) {
+       case BPF_MAP_TYPE_SOCKMAP:
+       case BPF_MAP_TYPE_SOCKHASH:
+               if (*arg_type == ARG_PTR_TO_MAP_VALUE) {
+@@ -9817,7 +9818,7 @@ skip_type_check:
+       switch (base_type(arg_type)) {
+       case ARG_CONST_MAP_PTR:
+               /* bpf_map_xxx(map_ptr) call: remember that map_ptr */
+-              if (meta->map_ptr) {
++              if (meta->map.ptr) {
+                       /* Use map_uid (which is unique id of inner map) to reject:
+                        * inner_map1 = bpf_map_lookup_elem(outer_map, key1)
+                        * inner_map2 = bpf_map_lookup_elem(outer_map, key2)
+@@ -9830,23 +9831,23 @@ skip_type_check:
+                        *
+                        * Comparing map_ptr is enough to distinguish normal and outer maps.
+                        */
+-                      if (meta->map_ptr != reg->map_ptr ||
+-                          meta->map_uid != reg->map_uid) {
++                      if (meta->map.ptr != reg->map_ptr ||
++                          meta->map.uid != reg->map_uid) {
+                               verbose(env,
+                                       "timer pointer in R1 map_uid=%d doesn't match map pointer in R2 map_uid=%d\n",
+-                                      meta->map_uid, reg->map_uid);
++                                      meta->map.uid, reg->map_uid);
+                               return -EINVAL;
+                       }
+               }
+-              meta->map_ptr = reg->map_ptr;
+-              meta->map_uid = reg->map_uid;
++              meta->map.ptr = reg->map_ptr;
++              meta->map.uid = reg->map_uid;
+               break;
+       case ARG_PTR_TO_MAP_KEY:
+               /* bpf_map_xxx(..., map_ptr, ..., key) call:
+                * check that [key, key + map->key_size) are within
+                * stack limits and initialized
+                */
+-              if (!meta->map_ptr) {
++              if (!meta->map.ptr) {
+                       /* in function declaration map_ptr must come before
+                        * map_key, so that it's verified and known before
+                        * we have to check map_key here. Otherwise it means
+@@ -9855,11 +9856,11 @@ skip_type_check:
+                       verifier_bug(env, "invalid map_ptr to access map->key");
+                       return -EFAULT;
+               }
+-              key_size = meta->map_ptr->key_size;
++              key_size = meta->map.ptr->key_size;
+               err = check_helper_mem_access(env, regno, key_size, BPF_READ, false, NULL);
+               if (err)
+                       return err;
+-              if (can_elide_value_nullness(meta->map_ptr->map_type)) {
++              if (can_elide_value_nullness(meta->map.ptr->map_type)) {
+                       err = get_constant_map_key(env, reg, key_size, &meta->const_map_key);
+                       if (err < 0) {
+                               meta->const_map_key = -1;
+@@ -9877,13 +9878,13 @@ skip_type_check:
+               /* bpf_map_xxx(..., map_ptr, ..., value) call:
+                * check [value, value + map->value_size) validity
+                */
+-              if (!meta->map_ptr) {
++              if (!meta->map.ptr) {
+                       /* kernel subsystem misconfigured verifier */
+                       verifier_bug(env, "invalid map_ptr to access map->value");
+                       return -EFAULT;
+               }
+               meta->raw_mode = arg_type & MEM_UNINIT;
+-              err = check_helper_mem_access(env, regno, meta->map_ptr->value_size,
++              err = check_helper_mem_access(env, regno, meta->map.ptr->value_size,
+                                             arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ,
+                                             false, meta);
+               break;
+@@ -11201,7 +11202,7 @@ record_func_map(struct bpf_verifier_env
+               int func_id, int insn_idx)
+ {
+       struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
+-      struct bpf_map *map = meta->map_ptr;
++      struct bpf_map *map = meta->map.ptr;
+       if (func_id != BPF_FUNC_tail_call &&
+           func_id != BPF_FUNC_map_lookup_elem &&
+@@ -11234,11 +11235,11 @@ record_func_map(struct bpf_verifier_env
+       }
+       if (!aux->map_ptr_state.map_ptr)
+-              bpf_map_ptr_store(aux, meta->map_ptr,
+-                                !meta->map_ptr->bypass_spec_v1, false);
+-      else if (aux->map_ptr_state.map_ptr != meta->map_ptr)
+-              bpf_map_ptr_store(aux, meta->map_ptr,
+-                                !meta->map_ptr->bypass_spec_v1, true);
++              bpf_map_ptr_store(aux, meta->map.ptr,
++                                !meta->map.ptr->bypass_spec_v1, false);
++      else if (aux->map_ptr_state.map_ptr != meta->map.ptr)
++              bpf_map_ptr_store(aux, meta->map.ptr,
++                                !meta->map.ptr->bypass_spec_v1, true);
+       return 0;
+ }
+@@ -11248,7 +11249,7 @@ record_func_key(struct bpf_verifier_env
+ {
+       struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
+       struct bpf_reg_state *reg;
+-      struct bpf_map *map = meta->map_ptr;
++      struct bpf_map *map = meta->map.ptr;
+       u64 val, max;
+       int err;
+@@ -11814,22 +11815,22 @@ static int check_helper_call(struct bpf_
+                * can check 'value_size' boundary of memory access
+                * to map element returned from bpf_map_lookup_elem()
+                */
+-              if (meta.map_ptr == NULL) {
++              if (meta.map.ptr == NULL) {
+                       verifier_bug(env, "unexpected null map_ptr");
+                       return -EFAULT;
+               }
+               if (func_id == BPF_FUNC_map_lookup_elem &&
+-                  can_elide_value_nullness(meta.map_ptr->map_type) &&
++                  can_elide_value_nullness(meta.map.ptr->map_type) &&
+                   meta.const_map_key >= 0 &&
+-                  meta.const_map_key < meta.map_ptr->max_entries)
++                  meta.const_map_key < meta.map.ptr->max_entries)
+                       ret_flag &= ~PTR_MAYBE_NULL;
+-              regs[BPF_REG_0].map_ptr = meta.map_ptr;
+-              regs[BPF_REG_0].map_uid = meta.map_uid;
++              regs[BPF_REG_0].map_ptr = meta.map.ptr;
++              regs[BPF_REG_0].map_uid = meta.map.uid;
+               regs[BPF_REG_0].type = PTR_TO_MAP_VALUE | ret_flag;
+               if (!type_may_be_null(ret_flag) &&
+-                  btf_record_has_field(meta.map_ptr->record, BPF_SPIN_LOCK | BPF_RES_SPIN_LOCK)) {
++                  btf_record_has_field(meta.map.ptr->record, BPF_SPIN_LOCK | BPF_RES_SPIN_LOCK)) {
+                       regs[BPF_REG_0].id = ++env->id_gen;
+               }
+               break;
+@@ -11932,7 +11933,7 @@ static int check_helper_call(struct bpf_
+       if (type_may_be_null(regs[BPF_REG_0].type))
+               regs[BPF_REG_0].id = ++env->id_gen;
+-      if (helper_multiple_ref_obj_use(func_id, meta.map_ptr)) {
++      if (helper_multiple_ref_obj_use(func_id, meta.map.ptr)) {
+               verifier_bug(env, "func %s#%d sets ref_obj_id more than once",
+                            func_id_name(func_id), func_id);
+               return -EFAULT;
+@@ -11944,7 +11945,7 @@ static int check_helper_call(struct bpf_
+       if (is_ptr_cast_function(func_id) || is_dynptr_ref_function(func_id)) {
+               /* For release_reference() */
+               regs[BPF_REG_0].ref_obj_id = meta.ref_obj_id;
+-      } else if (is_acquire_function(func_id, meta.map_ptr)) {
++      } else if (is_acquire_function(func_id, meta.map.ptr)) {
+               int id = acquire_reference(env, insn_idx);
+               if (id < 0)
+@@ -11959,7 +11960,7 @@ static int check_helper_call(struct bpf_
+       if (err)
+               return err;
+-      err = check_map_func_compatibility(env, meta.map_ptr, func_id);
++      err = check_map_func_compatibility(env, meta.map.ptr, func_id);
+       if (err)
+               return err;
diff --git a/queue-6.18/bpf-keep-dynamic-inner-array-lookups-nullable.patch b/queue-6.18/bpf-keep-dynamic-inner-array-lookups-nullable.patch
new file mode 100644 (file)
index 0000000..ee7a18f
--- /dev/null
@@ -0,0 +1,87 @@
+From stable+bounces-277300-greg=kroah.com@vger.kernel.org Sat Jul 18 13:45:10 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 07:44:34 -0400
+Subject: bpf: Keep dynamic inner array lookups nullable
+To: stable@vger.kernel.org
+Cc: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>, Eduard Zingerman <eddyz87@gmail.com>, Jiri Olsa <jolsa@kernel.org>, Kumar Kartikeya Dwivedi <memxor@gmail.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718114435.3046796-3-sashal@kernel.org>
+
+From: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>
+
+[ Upstream commit 53040a81ae57cdca8af8ac36fe4e661730cf7c6b ]
+
+An ARRAY_OF_MAPS can use an array created with BPF_F_INNER_MAP as its
+inner map template. A concrete inner array with a different max_entries
+value can then replace the template.
+
+After a successful outer map lookup, the verifier represents the
+resulting map pointer using the inner map template. Const-key lookup
+nullness elision consequently uses the template max_entries even though
+the runtime helper uses the concrete inner map max_entries.
+
+Do not elide lookup result nullness for maps marked with BPF_F_INNER_MAP,
+because the template max_entries does not prove that the key is in bounds
+for the concrete runtime map.
+
+Fixes: d2102f2f5d75 ("bpf: verifier: Support eliding map lookup nullness")
+Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>
+Acked-by: Eduard Zingerman <eddyz87@gmail.com>
+Acked-by: Jiri Olsa <jolsa@kernel.org>
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/bpf/20260607-f01-v2-v2-1-da48453146e8@mails.tsinghua.edu.cn
+Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ kernel/bpf/verifier.c |   15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -9710,7 +9710,7 @@ static int get_constant_map_key(struct b
+       return 0;
+ }
+-static bool can_elide_value_nullness(enum bpf_map_type type);
++static bool can_elide_value_nullness(const struct bpf_map *map);
+ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
+                         struct bpf_call_arg_meta *meta,
+@@ -9860,7 +9860,7 @@ skip_type_check:
+               err = check_helper_mem_access(env, regno, key_size, BPF_READ, false, NULL);
+               if (err)
+                       return err;
+-              if (can_elide_value_nullness(meta->map.ptr->map_type)) {
++              if (can_elide_value_nullness(meta->map.ptr)) {
+                       err = get_constant_map_key(env, reg, key_size, &meta->const_map_key);
+                       if (err < 0) {
+                               meta->const_map_key = -1;
+@@ -11433,13 +11433,16 @@ static void update_loop_inline_state(str
+                                state->callback_subprogno == subprogno);
+ }
+-/* Returns whether or not the given map type can potentially elide
++/* Returns whether or not the given map can potentially elide
+  * lookup return value nullness check. This is possible if the key
+  * is statically known.
+  */
+-static bool can_elide_value_nullness(enum bpf_map_type type)
++static bool can_elide_value_nullness(const struct bpf_map *map)
+ {
+-      switch (type) {
++      if (map->map_flags & BPF_F_INNER_MAP)
++              return false;
++
++      switch (map->map_type) {
+       case BPF_MAP_TYPE_ARRAY:
+       case BPF_MAP_TYPE_PERCPU_ARRAY:
+               return true;
+@@ -11821,7 +11824,7 @@ static int check_helper_call(struct bpf_
+               }
+               if (func_id == BPF_FUNC_map_lookup_elem &&
+-                  can_elide_value_nullness(meta.map.ptr->map_type) &&
++                  can_elide_value_nullness(meta.map.ptr) &&
+                   meta.const_map_key >= 0 &&
+                   meta.const_map_key < meta.map.ptr->max_entries)
+                       ret_flag &= ~PTR_MAYBE_NULL;
diff --git a/queue-6.18/btrfs-concentrate-the-error-handling-of-submit_one_sector.patch b/queue-6.18/btrfs-concentrate-the-error-handling-of-submit_one_sector.patch
new file mode 100644 (file)
index 0000000..fc329de
--- /dev/null
@@ -0,0 +1,95 @@
+From stable+bounces-277912-greg=kroah.com@vger.kernel.org Mon Jul 20 17:25:39 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:01:06 -0400
+Subject: btrfs: concentrate the error handling of submit_one_sector()
+To: stable@vger.kernel.org
+Cc: Qu Wenruo <wqu@suse.com>, Boris Burkov <boris@bur.io>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720150109.1921272-1-sashal@kernel.org>
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 44820d80026e0b509007d41c83d42f1213ee8589 ]
+
+Currently submit_one_sector() has only one failure path from
+btrfs_get_extent().
+
+However the error handling is split into two parts, one inside
+submit_one_sector(), which clears the dirty flag and finishes the
+writeback for the fs block.
+
+The other part is to submit any remaining bio inside bio_ctrl and mark
+the ordered extent finished for the fs block.
+
+There is no special reason that we must split the error handling, let's
+just concentrate all the error handling into submit_one_sector().
+
+Reviewed-by: Boris Burkov <boris@bur.io>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent_io.c |   29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+--- a/fs/btrfs/extent_io.c
++++ b/fs/btrfs/extent_io.c
+@@ -1599,7 +1599,7 @@ out:
+ /*
+  * Return 0 if we have submitted or queued the sector for submission.
+- * Return <0 for critical errors, and the sector will have its dirty flag cleared.
++ * Return <0 for critical errors, and the involved sector will be cleaned up.
+  *
+  * Caller should make sure filepos < i_size and handle filepos >= i_size case.
+  */
+@@ -1624,6 +1624,13 @@ static int submit_one_sector(struct btrf
+       em = btrfs_get_extent(inode, NULL, filepos, sectorsize);
+       if (IS_ERR(em)) {
+               /*
++               * bio_ctrl may contain a bio crossing several folios.
++               * Submit it immediately so that the bio has a chance
++               * to finish normally, other than marked as error.
++               */
++              submit_one_bio(bio_ctrl);
++
++              /*
+                * When submission failed, we should still clear the folio dirty.
+                * Or the folio will be written back again but without any
+                * ordered extent.
+@@ -1631,6 +1638,13 @@ static int submit_one_sector(struct btrf
+               btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
+               btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize);
+               btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize);
++
++              /*
++               * Since there is no bio submitted to finish the ordered
++               * extent, we have to manually finish this sector.
++               */
++              btrfs_mark_ordered_io_finished(inode, folio, filepos,
++                                             fs_info->sectorsize, false);
+               return PTR_ERR(em);
+       }
+@@ -1756,19 +1770,6 @@ static noinline_for_stack int extent_wri
+               }
+               ret = submit_one_sector(inode, folio, cur, bio_ctrl, i_size);
+               if (unlikely(ret < 0)) {
+-                      /*
+-                       * bio_ctrl may contain a bio crossing several folios.
+-                       * Submit it immediately so that the bio has a chance
+-                       * to finish normally, other than marked as error.
+-                       */
+-                      submit_one_bio(bio_ctrl);
+-                      /*
+-                       * Failed to grab the extent map which should be very rare.
+-                       * Since there is no bio submitted to finish the ordered
+-                       * extent, we have to manually finish this sector.
+-                       */
+-                      btrfs_mark_ordered_io_finished(inode, folio, cur,
+-                                                     fs_info->sectorsize, false);
+                       if (!found_error)
+                               found_error = ret;
+                       continue;
diff --git a/queue-6.18/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch b/queue-6.18/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch
new file mode 100644 (file)
index 0000000..f114191
--- /dev/null
@@ -0,0 +1,189 @@
+From stable+bounces-277915-greg=kroah.com@vger.kernel.org Mon Jul 20 17:49:25 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:01:09 -0400
+Subject: btrfs: fix false IO failure after falling back to buffered write
+To: stable@vger.kernel.org
+Cc: Qu Wenruo <wqu@suse.com>, Boris Burkov <boris@bur.io>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720150109.1921272-4-sashal@kernel.org>
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 66ff4d366e7eb4d31813d2acabf3af512ce03aa5 ]
+
+[BUG]
+The test case generic/362 will fail with "nodatasum" mount option (*):
+
+ MOUNT_OPTIONS -- -o nodatasum /dev/mapper/test-scratch1 /mnt/scratch
+
+# generic/362  0s ... - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad)
+#    --- tests/generic/362.out 2024-08-24 15:31:37.200000000 +0930
+#    +++ /home/adam/xfstests/results//generic/362.out.bad      2026-05-27 10:21:17.574771567 +0930
+#    @@ -1,2 +1,3 @@
+#     QA output created by 362
+#    +First write failed: Input/output error
+#     Silence is golden
+#    ...
+
+*: If the test case has been executed before with default data checksum,
+the failure will not reproduce. Need the following fix to make it
+reliably reproducible:
+https://lore.kernel.org/linux-btrfs/20260528111659.87113-1-wqu@suse.com/
+
+[CAUSE]
+Inside __iomap_dio_rw(), the -EFAULT/-ENOTBLK error is not directly returned.
+Thus we never got an error pointer from __iomap_dio_rw().
+
+The call chain looks like this:
+
+ btrfs_direct_write()
+ |- btrfs_dio_write()
+ |-  __iomap_dio_rw()
+ |  |- iomap_iter()
+ |  |  |- btrfs_dio_iomap_begin()
+ |  |     Now an ordered extent is allocated for the 4K write.
+ |  |
+ |  |- iomi.status = iomap_dio_iter()
+ |  |  Where iomap_dio_iter() returned -EFAULT.
+ |  |
+ |  |- ret = iomap_iter()
+ |  |  |- btrfs_dio_iomap_end()
+ |  |  |  |- btrfs_finish_ordered_extent(uptodate = false)
+ |  |  |  |  |- can_finish_ordered_extent()
+ |  |  |  |     |- btrfs_mark_ordered_extent_error()
+ |  |  |  |        |- mapping_set_error()
+ |  |  |  |           Now the address space is marked error.
+ |  |  |  | return -ENOTBLK
+ |  |  |- return -ENOTBLK
+ |  |- if (ret == -ENOTBLK) { ret = 0; }
+ |     Now the return value is reset to 0.
+ |     Thus no error pointer will be returned.
+ |
+ |- ret = iomap_dio_complete()
+ |  Since no byte is submitted, @ret is 0.
+ |
+ |- Fallback to buffered IO
+ |  And the buffered write finished without error
+ |
+ |- filemap_fdatawait_range()
+    |- filemap_check_errors()
+       The previous error is recorded, thus an error is returned
+
+However the buffered write is properly submitted and finished, the error
+is from the btrfs_finish_ordered_extent() call with @uptodate = false.
+
+[FIX]
+When a short dio write happened, any range that is submitted will have
+btrfs_extract_ordered_extent() to be called, thus the submitted range
+will always have an OE just covering the submitted range.
+
+The remaining OE range is never submitted, thus they should be treated
+as truncated, not an error. So that we can properly reclaim and not
+insert an unnecessary file extent item, without marking the mapping as
+error.
+
+Extract a helper, btrfs_mark_ordered_extent_truncated(), and utilize
+that helper to mark the direct IO ordered extent as truncated, so it
+won't cause failure for the later buffered fallback.
+
+[REASON FOR NO FIXES TAG]
+The bug itself is pretty old, at commit f85781fb505e ("btrfs: switch to
+iomap for direct IO") we're already passing @uptodate=false finishing
+the OE.
+But at that time OE with IOERR won't call mapping_set_error(), so it's
+not exposed.
+Later commit d61bec08b904 ("btrfs: mark ordered extent and inode with
+error if we fail to finish") finally exposed the bug, but that commit
+is doing a correct job, not the root cause.
+
+Anyway the bug is very old, dating back to 5.1x days, thus only CC to
+stable.
+
+CC: stable@vger.kernel.org # 5.15+
+Reviewed-by: Boris Burkov <boris@bur.io>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/direct-io.c    |   17 ++++++++++++++---
+ fs/btrfs/inode.c        |    6 +-----
+ fs/btrfs/ordered-data.c |   12 ++++++++++++
+ fs/btrfs/ordered-data.h |    2 ++
+ 4 files changed, 29 insertions(+), 8 deletions(-)
+
+--- a/fs/btrfs/direct-io.c
++++ b/fs/btrfs/direct-io.c
+@@ -624,12 +624,23 @@ static int btrfs_dio_iomap_end(struct in
+       if (submitted < length) {
+               pos += submitted;
+               length -= submitted;
+-              if (write)
++              if (write) {
++                      /*
++                       * We have a short write, if there is any range
++                       * that is submitted properly, that part will have
++                       * its own OE split from the original one.
++                       *
++                       * So for the OE at dio_data->ordered, it's the part
++                       * that is not submitted, and should be marked
++                       * as fully truncated.
++                       */
++                      btrfs_mark_ordered_extent_truncated(dio_data->ordered, 0);
+                       btrfs_finish_ordered_extent(dio_data->ordered,
+-                                                  pos, length, false);
+-              else
++                                                  pos, length, true);
++              } else {
+                       btrfs_unlock_dio_extent(&BTRFS_I(inode)->io_tree, pos,
+                                               pos + length - 1, NULL);
++              }
+               ret = -ENOTBLK;
+       }
+       if (write) {
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -7681,11 +7681,7 @@ static void btrfs_invalidate_folio(struc
+                                              EXTENT_LOCKED | EXTENT_DO_ACCOUNTING |
+                                              EXTENT_DEFRAG, &cached_state);
+-              spin_lock(&inode->ordered_tree_lock);
+-              set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
+-              ordered->truncated_len = min(ordered->truncated_len,
+-                                           cur - ordered->file_offset);
+-              spin_unlock(&inode->ordered_tree_lock);
++              btrfs_mark_ordered_extent_truncated(ordered, cur - ordered->file_offset);
+               /*
+                * If the ordered extent has finished, we're safe to delete all
+--- a/fs/btrfs/ordered-data.c
++++ b/fs/btrfs/ordered-data.c
+@@ -339,6 +339,18 @@ void btrfs_mark_ordered_extent_error(str
+               mapping_set_error(ordered->inode->vfs_inode.i_mapping, -EIO);
+ }
++void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered,
++                                       u64 truncate_len)
++{
++      struct btrfs_inode *inode = ordered->inode;
++
++      ASSERT(truncate_len <= ordered->num_bytes);
++      spin_lock(&inode->ordered_tree_lock);
++      set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
++      ordered->truncated_len = min(ordered->truncated_len, truncate_len);
++      spin_unlock(&inode->ordered_tree_lock);
++}
++
+ static void finish_ordered_fn(struct btrfs_work *work)
+ {
+       struct btrfs_ordered_extent *ordered_extent;
+--- a/fs/btrfs/ordered-data.h
++++ b/fs/btrfs/ordered-data.h
+@@ -220,6 +220,8 @@ bool btrfs_try_lock_ordered_range(struct
+ struct btrfs_ordered_extent *btrfs_split_ordered_extent(
+                       struct btrfs_ordered_extent *ordered, u64 len);
+ void btrfs_mark_ordered_extent_error(struct btrfs_ordered_extent *ordered);
++void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered,
++                                       u64 truncate_len);
+ int __init ordered_data_init(void);
+ void __cold ordered_data_exit(void);
diff --git a/queue-6.18/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch b/queue-6.18/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch
new file mode 100644 (file)
index 0000000..8ccfb3d
--- /dev/null
@@ -0,0 +1,200 @@
+From stable+bounces-278149-greg=kroah.com@vger.kernel.org Mon Jul 20 19:39:15 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 12:51:15 -0400
+Subject: btrfs: fix incorrect buffered IO fallback for append direct writes
+To: stable@vger.kernel.org
+Cc: Qu Wenruo <wqu@suse.com>, Boris Burkov <boris@bur.io>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720165115.2527045-5-sashal@kernel.org>
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit ff66fe6662330226b3f486014c375538d91c44aa ]
+
+[BUG]
+With the previous bug of short direct writes fixed, test case
+generic/362 (*) still fails with the following error with nodatasum
+mount option:
+
+# generic/362  0s ... - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad)
+# - output mismatch (see /home/adam/xfstests/results//generic/362.out.bad)
+#    --- tests/generic/362.out 2024-08-24 15:31:37.200000000 +0930
+#    +++ /home/adam/xfstests/results//generic/362.out.bad      2026-05-27 10:13:09.072485767 +0930
+#    @@ -1,2 +1,3 @@
+#     QA output created by 362
+#    +Wrong file size after first write, got 8192 expected 4096
+#     Silence is golden
+#    ...
+
+*: If the test case has been executed before with default data checksum,
+the failure will not reproduce. Need the following fix to make it
+reliably reproducible:
+https://lore.kernel.org/linux-btrfs/20260528111659.87113-1-wqu@suse.com/
+
+[CAUSE]
+Inside btrfs_dio_iomap_begin() for a direct write, we increase the isize
+if it's beyond the current isize.
+
+But if the direct io finished short, we do not revert the isize to the
+previous value nor to the short write end.
+
+Then if we need to fall back to buffered writes, and the write has
+IOCB_APPEND flag, then the buffered write will be positioned at the
+incorrect isize.
+
+The call chain looks like this:
+
+ btrfs_direct_write(pos=0, length=4K)
+ |- __iomap_dio_rw()
+ |  |- iomap_iter()
+ |  |  |- btrfs_dio_iomap_begin()
+ |  |     |- btrfs_get_blocks_direct_write()
+ |  |        |- i_size_write()
+ |  |           Which updates the isize to the write end (4K).
+ |  |
+ |  |- iomap_dio_iter()
+ |  |  Failed with -EFAULT on the first page.
+ |  |
+ |  |- iomap_iter()
+ |  |  |- btrfs_dio_iomap_end()
+ |  |     Detects a short write, return -ENOTBLK
+ |  |- if (ret == -ENOTBLK) { ret = 0;}
+ |     Which resets the return value.
+ |
+ |- ret = iomap_dio_complet()
+ |  Which returns 0.
+ |
+ |- btrfs_buffered_write(iocb, from);
+    |- generic_write_checks()
+       |- iocb->ki_pos = i_size_read()
+          Which is still the new size (4K), other than the original
+         isize 0.
+
+[FIX]
+Introduce the following btrfs_dio_data members:
+
+- old_isize
+
+- updated_isize
+  If the direct write has enlarged the isize.
+
+Then if we got a short write, and btrfs_dio_data::updated_isize is set,
+revert to the correct isize based on old_isize and current file
+position.
+
+And here we call i_size_write() without holding an extent lock, which is
+a very special case that we're safe to do:
+
+ - Only a single writer can be enlarging isize
+   Enlarging isize will take the exclusive inode lock.
+
+ - Buffered readers need to wait for the OE we're holding
+   Buffered readers will lock extent and wait for OE of the folio range.
+   Sometimes we can skip the OE wait, but since all page cache is
+   invalidated, the OE wait can not be skipped.
+
+But I do not think this is the most elegant solution, nor covers all
+cases. E.g. if the bio is submitted but IO failed, we are unable to do
+the revert.
+
+I believe the more elegant one would be extend the EXTENT_DIO_LOCKED
+lifespan for direct writes, so that we can update the isize when a
+write beyond EOF finished successfully.
+
+However that change is too huge for a small bug fix.
+So only implement the minimal partial fix for now.
+
+[REASON FOR NO FIXES TAG]
+The bug is again very old, before commit f85781fb505e ("btrfs: switch to
+iomap for direct IO") we are already increasing isize without a
+proper rollback for short writes.
+
+Thus only a CC to stable.
+
+CC: stable@vger.kernel.org # 5.15+
+Reviewed-by: Boris Burkov <boris@bur.io>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/direct-io.c |   43 ++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 42 insertions(+), 1 deletion(-)
+
+--- a/fs/btrfs/direct-io.c
++++ b/fs/btrfs/direct-io.c
+@@ -15,10 +15,12 @@
+ struct btrfs_dio_data {
+       ssize_t submitted;
++      loff_t old_isize;
+       struct extent_changeset *data_reserved;
+       struct btrfs_ordered_extent *ordered;
+       bool data_space_reserved;
+       bool nocow_done;
++      bool updated_isize;
+ };
+ struct btrfs_dio_private {
+@@ -228,6 +230,7 @@ static int btrfs_get_blocks_direct_write
+       bool space_reserved = false;
+       u64 len = *lenp;
+       u64 prev_len;
++      loff_t old_isize;
+       int ret = 0;
+       /*
+@@ -341,8 +344,14 @@ static int btrfs_get_blocks_direct_write
+        * Need to update the i_size under the extent lock so buffered
+        * readers will get the updated i_size when we unlock.
+        */
+-      if (start + len > i_size_read(inode))
++      old_isize = i_size_read(inode);
++      if (start + len > old_isize) {
++              if (!dio_data->updated_isize) {
++                      dio_data->old_isize = old_isize;
++                      dio_data->updated_isize = true;
++              }
+               i_size_write(inode, start + len);
++      }
+ out:
+       if (ret && space_reserved) {
+               btrfs_delalloc_release_extents(BTRFS_I(inode), len);
+@@ -626,6 +635,38 @@ static int btrfs_dio_iomap_end(struct in
+               length -= submitted;
+               if (write) {
+                       /*
++                       * Got a short write and have updated the isize, need to
++                       * revert the isize change.
++                       *
++                       * Normally we need to update isize with extent lock hold,
++                       * but we're safe due to the following factors:
++                       *
++                       * - Only a single writer can be enlarging isize
++                       *   Enlarging isize will take the exclusive inode lock.
++                       *
++                       * - Buffered readers need to wait for the OE we're holding
++                       *   Buffered readers will lock extent and wait for OE
++                       *   of the folio range, and since page cache is invalidated
++                       *   the OE wait can not be skipped.
++                       *
++                       * So here we are safe to revert the isize before
++                       * finishing the OE, and no reader of the remaining range
++                       * can see the enlarged size.
++                       *
++                       * TODO: Extend the DIO_LOCKED lifespan for direct writes,
++                       * and only enlarge isize after a successful write.
++                       */
++                      if (dio_data->updated_isize) {
++                              u64 new_isize;
++
++                              if (submitted == 0)
++                                      new_isize = dio_data->old_isize;
++                              else
++                                      new_isize = max(dio_data->old_isize, pos);
++                              i_size_write(inode, new_isize);
++                              dio_data->updated_isize = false;
++                      }
++                      /*
+                        * We have a short write, if there is any range
+                        * that is submitted properly, that part will have
+                        * its own OE split from the original one.
diff --git a/queue-6.18/btrfs-remove-folio-parameter-from-ordered-io-related-functions.patch b/queue-6.18/btrfs-remove-folio-parameter-from-ordered-io-related-functions.patch
new file mode 100644 (file)
index 0000000..3d18bea
--- /dev/null
@@ -0,0 +1,354 @@
+From stable+bounces-277914-greg=kroah.com@vger.kernel.org Mon Jul 20 17:25:41 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:01:08 -0400
+Subject: btrfs: remove folio parameter from ordered io related functions
+To: stable@vger.kernel.org
+Cc: Qu Wenruo <wqu@suse.com>, Filipe Manana <fdmanana@suse.com>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720150109.1921272-3-sashal@kernel.org>
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit a11d6912fdd9e57aff889ec97256b1d6b4e5bf06 ]
+
+Both functions btrfs_finish_ordered_extent() and
+btrfs_mark_ordered_io_finished() are accepting an optional folio
+parameter.
+
+That @folio is passed into can_finish_ordered_extent(), which later will
+test and clear the ordered flag for the involved range.
+
+However I do not think there is any other call site that can clear
+ordered flags of an page cache folio and can affect
+can_finish_ordered_extent().
+
+There are limited *_clear_ordered() callers out of
+can_finish_ordered_extent() function:
+
+- btrfs_migrate_folio()
+  This is completely unrelated, it's just migrating the ordered flag to
+  the new folio.
+
+- btrfs_cleanup_ordered_extents()
+  We manually clean the ordered flags of all involved folios, then call
+  btrfs_mark_ordered_io_finished() without a @folio parameter.
+  So it doesn't need and didn't pass a @folio parameter in the first
+  place.
+
+- btrfs_writepage_fixup_worker()
+  This function is going to be removed soon, and we should not hit that
+  function anymore.
+
+- btrfs_invalidate_folio()
+  This is the real call site we need to bother with.
+
+  If we already have a bio running, btrfs_finish_ordered_extent() in
+  end_bbio_data_write() will be executed first, as
+  btrfs_invalidate_folio() will wait for the writeback to finish.
+
+  Thus if there is a running bio, it will not see the range has
+  ordered flags, and just skip to the next range.
+
+  If there is no bio running, meaning the ordered extent is created but
+  the folio is not yet submitted.
+
+  In that case btrfs_invalidate_folio() will manually clear the folio
+  ordered range, but then manually finish the ordered extent with
+  btrfs_dec_test_ordered_pending() without bothering the folio ordered
+  flags.
+
+  Meaning if the OE range with folio ordered flags will be finished
+  manually without the need to call can_finish_ordered_extent().
+
+This means all can_finish_ordered_extent() call sites should get a range
+that has folio ordered flag set, thus the old "return false" branch
+should never be triggered.
+
+Now we can:
+
+- Remove the @folio parameter from involved functions
+  * btrfs_mark_ordered_io_finished()
+  * btrfs_finish_ordered_extent()
+
+  For call sites passing a @folio into those functions, let them
+  manually clear the ordered flag of involved folios.
+
+- Move btrfs_finish_ordered_extent() out of the loop in
+  end_bbio_data_write()
+
+  We only need to call btrfs_finish_ordered_extent() once per bbio,
+  not per folio.
+
+- Add an ASSERT() to make sure all folio ranges have ordered flags
+  It's only for end_bbio_data_write().
+
+  And we already have enough safe nets to catch over-accounting of ordered
+  extents.
+
+Reviewed-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/compression.c  |    2 +-
+ fs/btrfs/direct-io.c    |    9 ++++-----
+ fs/btrfs/extent_io.c    |   23 ++++++++++++++---------
+ fs/btrfs/inode.c        |   10 ++++++----
+ fs/btrfs/ordered-data.c |   29 +++++------------------------
+ fs/btrfs/ordered-data.h |    6 ++----
+ 6 files changed, 32 insertions(+), 47 deletions(-)
+
+--- a/fs/btrfs/compression.c
++++ b/fs/btrfs/compression.c
+@@ -324,7 +324,7 @@ static void btrfs_finish_compressed_writ
+       struct compressed_bio *cb =
+               container_of(work, struct compressed_bio, write_end_work);
+-      btrfs_finish_ordered_extent(cb->bbio.ordered, NULL, cb->start, cb->len,
++      btrfs_finish_ordered_extent(cb->bbio.ordered, cb->start, cb->len,
+                                   cb->bbio.bio.bi_status == BLK_STS_OK);
+       if (cb->writeback)
+--- a/fs/btrfs/direct-io.c
++++ b/fs/btrfs/direct-io.c
+@@ -625,7 +625,7 @@ static int btrfs_dio_iomap_end(struct in
+               pos += submitted;
+               length -= submitted;
+               if (write)
+-                      btrfs_finish_ordered_extent(dio_data->ordered, NULL,
++                      btrfs_finish_ordered_extent(dio_data->ordered,
+                                                   pos, length, false);
+               else
+                       btrfs_unlock_dio_extent(&BTRFS_I(inode)->io_tree, pos,
+@@ -657,9 +657,8 @@ static void btrfs_dio_end_io(struct btrf
+       }
+       if (btrfs_op(bio) == BTRFS_MAP_WRITE) {
+-              btrfs_finish_ordered_extent(bbio->ordered, NULL,
+-                                          dip->file_offset, dip->bytes,
+-                                          !bio->bi_status);
++              btrfs_finish_ordered_extent(bbio->ordered, dip->file_offset,
++                                          dip->bytes, !bio->bi_status);
+       } else {
+               btrfs_unlock_dio_extent(&inode->io_tree, dip->file_offset,
+                                       dip->file_offset + dip->bytes - 1, NULL);
+@@ -735,7 +734,7 @@ static void btrfs_dio_submit_io(const st
+               ret = btrfs_extract_ordered_extent(bbio, dio_data->ordered);
+               if (ret) {
+-                      btrfs_finish_ordered_extent(dio_data->ordered, NULL,
++                      btrfs_finish_ordered_extent(dio_data->ordered,
+                                                   file_offset, dip->bytes,
+                                                   !ret);
+                       bio->bi_status = errno_to_blk_status(ret);
+--- a/fs/btrfs/extent_io.c
++++ b/fs/btrfs/extent_io.c
+@@ -523,6 +523,7 @@ static void end_bbio_data_write(struct b
+       int error = blk_status_to_errno(bio->bi_status);
+       struct folio_iter fi;
+       const u32 sectorsize = fs_info->sectorsize;
++      u32 bio_size = 0;
+       ASSERT(!bio_flagged(bio, BIO_CLONED));
+       bio_for_each_folio_all(fi, bio) {
+@@ -530,6 +531,7 @@ static void end_bbio_data_write(struct b
+               u64 start = folio_pos(folio) + fi.offset;
+               u32 len = fi.length;
++              bio_size += len;
+               /* Our read/write should always be sector aligned. */
+               if (!IS_ALIGNED(fi.offset, sectorsize))
+                       btrfs_err(fs_info,
+@@ -540,13 +542,15 @@ static void end_bbio_data_write(struct b
+               "incomplete page write with offset %zu and length %zu",
+                                  fi.offset, fi.length);
+-              btrfs_finish_ordered_extent(bbio->ordered, folio, start, len,
+-                                          !error);
+               if (error)
+                       mapping_set_error(folio->mapping, error);
++
++              ASSERT(btrfs_folio_test_ordered(fs_info, folio, start, len));
++              btrfs_folio_clear_ordered(fs_info, folio, start, len);
+               btrfs_folio_clear_writeback(fs_info, folio, start, len);
+       }
++      btrfs_finish_ordered_extent(bbio->ordered, bbio->file_offset, bio_size, !error);
+       bio_put(bio);
+ }
+@@ -1566,7 +1570,8 @@ static noinline_for_stack int writepage_
+                       u64 start = page_start + (start_bit << fs_info->sectorsize_bits);
+                       u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits;
+-                      btrfs_mark_ordered_io_finished(inode, folio, start, len, false);
++                      btrfs_folio_clear_ordered(fs_info, folio, start, len);
++                      btrfs_mark_ordered_io_finished(inode, start, len, false);
+               }
+               return ret;
+       }
+@@ -1642,6 +1647,7 @@ static int submit_one_sector(struct btrf
+                * ordered extent.
+                */
+               btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
++              btrfs_folio_clear_ordered(fs_info, folio, filepos, sectorsize);
+               btrfs_folio_set_writeback(fs_info, folio, filepos, sectorsize);
+               btrfs_folio_clear_writeback(fs_info, folio, filepos, sectorsize);
+@@ -1649,8 +1655,8 @@ static int submit_one_sector(struct btrf
+                * Since there is no bio submitted to finish the ordered
+                * extent, we have to manually finish this sector.
+                */
+-              btrfs_mark_ordered_io_finished(inode, folio, filepos,
+-                                             fs_info->sectorsize, false);
++              btrfs_mark_ordered_io_finished(inode, filepos, fs_info->sectorsize,
++                                             false);
+               return PTR_ERR(em);
+       }
+@@ -1761,8 +1767,8 @@ static noinline_for_stack int extent_wri
+                       spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
+                       btrfs_put_ordered_extent(ordered);
+-                      btrfs_mark_ordered_io_finished(inode, folio, cur,
+-                                                     fs_info->sectorsize, true);
++                      btrfs_folio_clear_ordered(fs_info, folio, cur, fs_info->sectorsize);
++                      btrfs_mark_ordered_io_finished(inode, cur, fs_info->sectorsize, true);
+                       /*
+                        * This range is beyond i_size, thus we don't need to
+                        * bother writing back.
+@@ -2640,8 +2646,7 @@ void extent_write_locked_range(struct in
+               if (IS_ERR(folio)) {
+                       cur_end = min(round_down(cur, PAGE_SIZE) + PAGE_SIZE - 1, end);
+                       cur_len = cur_end + 1 - cur;
+-                      btrfs_mark_ordered_io_finished(BTRFS_I(inode), NULL,
+-                                                     cur, cur_len, false);
++                      btrfs_mark_ordered_io_finished(BTRFS_I(inode), cur, cur_len, false);
+                       mapping_set_error(mapping, PTR_ERR(folio));
+                       cur = cur_end;
+                       continue;
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -424,7 +424,7 @@ static inline void btrfs_cleanup_ordered
+               folio_put(folio);
+       }
+-      return btrfs_mark_ordered_io_finished(inode, NULL, offset, bytes, false);
++      return btrfs_mark_ordered_io_finished(inode, offset, bytes, false);
+ }
+ static int btrfs_dirty_inode(struct btrfs_inode *inode);
+@@ -2864,7 +2864,9 @@ out_page:
+                * to reflect the errors and clean the page.
+                */
+               mapping_set_error(folio->mapping, ret);
+-              btrfs_mark_ordered_io_finished(inode, folio, page_start,
++              btrfs_folio_clear_ordered(fs_info, folio, page_start,
++                                        folio_size(folio));
++              btrfs_mark_ordered_io_finished(inode, page_start,
+                                              folio_size(folio), !ret);
+               folio_clear_dirty_for_io(folio);
+       }
+@@ -7679,11 +7681,11 @@ static void btrfs_invalidate_folio(struc
+                                              EXTENT_LOCKED | EXTENT_DO_ACCOUNTING |
+                                              EXTENT_DEFRAG, &cached_state);
+-              spin_lock_irq(&inode->ordered_tree_lock);
++              spin_lock(&inode->ordered_tree_lock);
+               set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags);
+               ordered->truncated_len = min(ordered->truncated_len,
+                                            cur - ordered->file_offset);
+-              spin_unlock_irq(&inode->ordered_tree_lock);
++              spin_unlock(&inode->ordered_tree_lock);
+               /*
+                * If the ordered extent has finished, we're safe to delete all
+--- a/fs/btrfs/ordered-data.c
++++ b/fs/btrfs/ordered-data.c
+@@ -348,30 +348,13 @@ static void finish_ordered_fn(struct btr
+ }
+ static bool can_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
+-                                    struct folio *folio, u64 file_offset,
+-                                    u64 len, bool uptodate)
++                                    u64 file_offset, u64 len, bool uptodate)
+ {
+       struct btrfs_inode *inode = ordered->inode;
+       struct btrfs_fs_info *fs_info = inode->root->fs_info;
+       lockdep_assert_held(&inode->ordered_tree_lock);
+-      if (folio) {
+-              ASSERT(folio->mapping);
+-              ASSERT(folio_pos(folio) <= file_offset);
+-              ASSERT(file_offset + len <= folio_end(folio));
+-
+-              /*
+-               * Ordered flag indicates whether we still have
+-               * pending io unfinished for the ordered extent.
+-               *
+-               * If it's not set, we need to skip to next range.
+-               */
+-              if (!btrfs_folio_test_ordered(fs_info, folio, file_offset, len))
+-                      return false;
+-              btrfs_folio_clear_ordered(fs_info, folio, file_offset, len);
+-      }
+-
+       /* Now we're fine to update the accounting. */
+       if (WARN_ON_ONCE(len > ordered->bytes_left)) {
+               btrfs_crit(fs_info,
+@@ -413,8 +396,7 @@ static void btrfs_queue_ordered_fn(struc
+ }
+ void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
+-                               struct folio *folio, u64 file_offset, u64 len,
+-                               bool uptodate)
++                               u64 file_offset, u64 len, bool uptodate)
+ {
+       struct btrfs_inode *inode = ordered->inode;
+       unsigned long flags;
+@@ -423,7 +405,7 @@ void btrfs_finish_ordered_extent(struct
+       trace_btrfs_finish_ordered_extent(inode, file_offset, len, uptodate);
+       spin_lock_irqsave(&inode->ordered_tree_lock, flags);
+-      ret = can_finish_ordered_extent(ordered, folio, file_offset, len,
++      ret = can_finish_ordered_extent(ordered, file_offset, len,
+                                       uptodate);
+       spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
+@@ -476,8 +458,7 @@ void btrfs_finish_ordered_extent(struct
+  * extent(s) covering it.
+  */
+ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
+-                                  struct folio *folio, u64 file_offset,
+-                                  u64 num_bytes, bool uptodate)
++                                  u64 file_offset, u64 num_bytes, bool uptodate)
+ {
+       struct rb_node *node;
+       struct btrfs_ordered_extent *entry = NULL;
+@@ -540,7 +521,7 @@ void btrfs_mark_ordered_io_finished(stru
+               ASSERT(end + 1 - cur < U32_MAX);
+               len = end + 1 - cur;
+-              if (can_finish_ordered_extent(entry, folio, cur, len, uptodate)) {
++              if (can_finish_ordered_extent(entry, cur, len, uptodate)) {
+                       spin_unlock_irqrestore(&inode->ordered_tree_lock, flags);
+                       btrfs_queue_ordered_fn(entry);
+                       spin_lock_irqsave(&inode->ordered_tree_lock, flags);
+--- a/fs/btrfs/ordered-data.h
++++ b/fs/btrfs/ordered-data.h
+@@ -164,11 +164,9 @@ void btrfs_put_ordered_extent(struct btr
+ void btrfs_remove_ordered_extent(struct btrfs_inode *btrfs_inode,
+                               struct btrfs_ordered_extent *entry);
+ void btrfs_finish_ordered_extent(struct btrfs_ordered_extent *ordered,
+-                               struct folio *folio, u64 file_offset, u64 len,
+-                               bool uptodate);
++                               u64 file_offset, u64 len, bool uptodate);
+ void btrfs_mark_ordered_io_finished(struct btrfs_inode *inode,
+-                                  struct folio *folio, u64 file_offset,
+-                                  u64 num_bytes, bool uptodate);
++                                  u64 file_offset, u64 num_bytes, bool uptodate);
+ bool btrfs_dec_test_ordered_pending(struct btrfs_inode *inode,
+                                   struct btrfs_ordered_extent **cached,
+                                   u64 file_offset, u64 io_size);
diff --git a/queue-6.18/btrfs-replace-for_each_set_bit-with-for_each_set_bitmap.patch b/queue-6.18/btrfs-replace-for_each_set_bit-with-for_each_set_bitmap.patch
new file mode 100644 (file)
index 0000000..e1b1596
--- /dev/null
@@ -0,0 +1,102 @@
+From stable+bounces-277913-greg=kroah.com@vger.kernel.org Mon Jul 20 17:25:40 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 11:01:07 -0400
+Subject: btrfs: replace for_each_set_bit() with for_each_set_bitmap()
+To: stable@vger.kernel.org
+Cc: Qu Wenruo <wqu@suse.com>, Boris Burkov <boris@bur.io>, David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720150109.1921272-2-sashal@kernel.org>
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit e6698b34fab33867ef3faeeea6feb165f31aae24 ]
+
+Inside extent_io.c, there are several simple call sites doing things
+like:
+
+       for_each_set_bit(bit, bitmap, bitmap_size) {
+               /* handle one fs block */
+       }
+
+The workload includes:
+
+- set_bit()
+  Inside extent_writepage_io().
+
+  This can be replaced with a bitmap_set().
+
+- btrfs_folio_set_lock()
+- btrfs_mark_ordered_io_finished()
+  Inside writepage_delalloc().
+
+  Instead of calling it multiple times, we can pass a range into the
+  function with one call.
+
+Reviewed-by: Boris Burkov <boris@bur.io>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: David Sterba <dsterba@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Stable-dep-of: 66ff4d366e7e ("btrfs: fix false IO failure after falling back to buffered write")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/extent_io.c |   26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+--- a/fs/btrfs/extent_io.c
++++ b/fs/btrfs/extent_io.c
+@@ -1427,8 +1427,9 @@ static noinline_for_stack int writepage_
+       u64 delalloc_start = page_start;
+       u64 delalloc_end = page_end;
+       u64 delalloc_to_write = 0;
++      unsigned int start_bit;
++      unsigned int end_bit;
+       int ret = 0;
+-      int bit;
+       /* Save the dirty bitmap as our submission bitmap will be a subset of it. */
+       if (btrfs_is_subpage(fs_info, folio)) {
+@@ -1438,10 +1439,12 @@ static noinline_for_stack int writepage_
+               bio_ctrl->submit_bitmap = 1;
+       }
+-      for_each_set_bit(bit, &bio_ctrl->submit_bitmap, blocks_per_folio) {
+-              u64 start = page_start + (bit << fs_info->sectorsize_bits);
++      for_each_set_bitrange(start_bit, end_bit, &bio_ctrl->submit_bitmap,
++                            blocks_per_folio) {
++              u64 start = page_start + (start_bit << fs_info->sectorsize_bits);
++              u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits;
+-              btrfs_folio_set_lock(fs_info, folio, start, fs_info->sectorsize);
++              btrfs_folio_set_lock(fs_info, folio, start, len);
+       }
+       /* Lock all (subpage) delalloc ranges inside the folio first. */
+@@ -1558,10 +1561,13 @@ static noinline_for_stack int writepage_
+                               fs_info->sectorsize_bits,
+                               blocks_per_folio);
+-              for_each_set_bit(bit, &bio_ctrl->submit_bitmap, bitmap_size)
+-                      btrfs_mark_ordered_io_finished(inode, folio,
+-                              page_start + (bit << fs_info->sectorsize_bits),
+-                              fs_info->sectorsize, false);
++              for_each_set_bitrange(start_bit, end_bit, &bio_ctrl->submit_bitmap,
++                                    bitmap_size) {
++                      u64 start = page_start + (start_bit << fs_info->sectorsize_bits);
++                      u32 len = (end_bit - start_bit) << fs_info->sectorsize_bits;
++
++                      btrfs_mark_ordered_io_finished(inode, folio, start, len, false);
++              }
+               return ret;
+       }
+ out:
+@@ -1727,8 +1733,8 @@ static noinline_for_stack int extent_wri
+               return ret;
+       }
+-      for (cur = start; cur < end; cur += fs_info->sectorsize)
+-              set_bit((cur - folio_start) >> fs_info->sectorsize_bits, &range_bitmap);
++      bitmap_set(&range_bitmap, (start - folio_pos(folio)) >> fs_info->sectorsize_bits,
++                 len >> fs_info->sectorsize_bits);
+       bitmap_and(&bio_ctrl->submit_bitmap, &bio_ctrl->submit_bitmap, &range_bitmap,
+                  blocks_per_folio);
diff --git a/queue-6.18/cifs-smb1-split-add-some-includes.patch b/queue-6.18/cifs-smb1-split-add-some-includes.patch
new file mode 100644 (file)
index 0000000..6065d38
--- /dev/null
@@ -0,0 +1,67 @@
+From stable+bounces-275227-greg=kroah.com@vger.kernel.org Thu Jul 16 12:23:46 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 06:18:18 -0400
+Subject: cifs: SMB1 split: Add some #includes
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Steve French <sfrench@samba.org>, Paulo Alcantara <pc@manguebit.org>, Enzo Matsumiya <ematsumiya@suse.de>, linux-cifs@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716101819.1461638-2-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit a7c7f35bcf0943ba85ab14bafbfbcbc3a30ad402 ]
+
+Add some #includes to make sure things continue to compile as splitting
+occurs.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Steve French <sfrench@samba.org>
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Enzo Matsumiya <ematsumiya@suse.de>
+cc: linux-cifs@vger.kernel.org
+cc: linux-fsdevel@vger.kernel.org
+cc: linux-kernel@vger.kernel.org
+Acked-by: Enzo Matsumiya <ematsumiya@suse.de>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: c6394bcaf254 ("ksmbd: use opener credentials for FSCTL mutations")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/cifssmb.c       |    5 +++--
+ fs/smb/client/smb1transport.c |    4 ++--
+ 2 files changed, 5 insertions(+), 4 deletions(-)
+
+--- a/fs/smb/client/cifssmb.c
++++ b/fs/smb/client/cifssmb.c
+@@ -26,11 +26,12 @@
+ #include <linux/uaccess.h>
+ #include <linux/netfs.h>
+ #include <trace/events/netfs.h>
++#include "cifsglob.h"
++#include "cifsproto.h"
++#include "../common/smbfsctl.h"
+ #include "cifspdu.h"
+ #include "cifsfs.h"
+-#include "cifsglob.h"
+ #include "cifsacl.h"
+-#include "cifsproto.h"
+ #include "cifs_unicode.h"
+ #include "cifs_debug.h"
+ #include "fscache.h"
+--- a/fs/smb/client/smb1transport.c
++++ b/fs/smb/client/smb1transport.c
+@@ -22,13 +22,13 @@
+ #include <linux/mempool.h>
+ #include <linux/sched/signal.h>
+ #include <linux/task_io_accounting_ops.h>
+-#include "cifspdu.h"
+ #include "cifsglob.h"
+ #include "cifsproto.h"
+-#include "cifs_debug.h"
++#include "cifspdu.h"
+ #include "smb2proto.h"
+ #include "smbdirect.h"
+ #include "compress.h"
++#include "cifs_debug.h"
+ /* Max number of iovectors we can use off the stack when sending requests. */
+ #define CIFS_MAX_IOV_SIZE 8
diff --git a/queue-6.18/cifs-smb1-split-rename-cifstransport.c.patch b/queue-6.18/cifs-smb1-split-rename-cifstransport.c.patch
new file mode 100644 (file)
index 0000000..e102d81
--- /dev/null
@@ -0,0 +1,1185 @@
+From stable+bounces-275226-greg=kroah.com@vger.kernel.org Thu Jul 16 12:23:22 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 06:18:17 -0400
+Subject: cifs: SMB1 split: Rename cifstransport.c
+To: stable@vger.kernel.org
+Cc: David Howells <dhowells@redhat.com>, Steve French <sfrench@samba.org>, Paulo Alcantara <pc@manguebit.org>, Enzo Matsumiya <ematsumiya@suse.de>, linux-cifs@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716101819.1461638-1-sashal@kernel.org>
+
+From: David Howells <dhowells@redhat.com>
+
+[ Upstream commit b09eab52b307df58a36f34d047378053a2e13e13 ]
+
+Rename cifstransport.c to smb1transport.c in order to give consistent names
+SMB1-specific files.
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+cc: Steve French <sfrench@samba.org>
+cc: Paulo Alcantara <pc@manguebit.org>
+cc: Enzo Matsumiya <ematsumiya@suse.de>
+cc: linux-cifs@vger.kernel.org
+cc: linux-fsdevel@vger.kernel.org
+cc: linux-kernel@vger.kernel.org
+Acked-by: Enzo Matsumiya <ematsumiya@suse.de>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: c6394bcaf254 ("ksmbd: use opener credentials for FSCTL mutations")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/Makefile                             | 5 ++++-
+ fs/smb/client/{cifstransport.c => smb1transport.c} | 0
+ fs/smb/client/Makefile        |    5 
+ fs/smb/client/cifstransport.c |  565 ------------------------------------------
+ fs/smb/client/smb1transport.c |  565 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 569 insertions(+), 566 deletions(-)
+ rename fs/smb/client/{cifstransport.c => smb1transport.c} (100%)
+
+--- a/fs/smb/client/Makefile
++++ b/fs/smb/client/Makefile
+@@ -32,6 +32,9 @@ cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdir
+ cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o
+-cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += smb1ops.o cifssmb.o cifstransport.o
++cifs-$(CONFIG_CIFS_ALLOW_INSECURE_LEGACY) += \
++      cifssmb.o \
++      smb1ops.o \
++      smb1transport.o
+ cifs-$(CONFIG_CIFS_COMPRESSION) += compress.o compress/lz77.o
+--- a/fs/smb/client/cifstransport.c
++++ /dev/null
+@@ -1,565 +0,0 @@
+-// SPDX-License-Identifier: LGPL-2.1
+-/*
+- *
+- *   Copyright (C) International Business Machines  Corp., 2002,2008
+- *   Author(s): Steve French (sfrench@us.ibm.com)
+- *   Jeremy Allison (jra@samba.org) 2006.
+- *
+- */
+-
+-#include <linux/fs.h>
+-#include <linux/list.h>
+-#include <linux/gfp.h>
+-#include <linux/wait.h>
+-#include <linux/net.h>
+-#include <linux/delay.h>
+-#include <linux/freezer.h>
+-#include <linux/tcp.h>
+-#include <linux/bvec.h>
+-#include <linux/highmem.h>
+-#include <linux/uaccess.h>
+-#include <linux/processor.h>
+-#include <linux/mempool.h>
+-#include <linux/sched/signal.h>
+-#include <linux/task_io_accounting_ops.h>
+-#include "cifspdu.h"
+-#include "cifsglob.h"
+-#include "cifsproto.h"
+-#include "cifs_debug.h"
+-#include "smb2proto.h"
+-#include "smbdirect.h"
+-#include "compress.h"
+-
+-/* Max number of iovectors we can use off the stack when sending requests. */
+-#define CIFS_MAX_IOV_SIZE 8
+-
+-static struct mid_q_entry *
+-alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
+-{
+-      struct mid_q_entry *temp;
+-
+-      if (server == NULL) {
+-              cifs_dbg(VFS, "%s: null TCP session\n", __func__);
+-              return NULL;
+-      }
+-
+-      temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
+-      memset(temp, 0, sizeof(struct mid_q_entry));
+-      kref_init(&temp->refcount);
+-      spin_lock_init(&temp->mid_lock);
+-      temp->mid = get_mid(smb_buffer);
+-      temp->pid = current->pid;
+-      temp->command = cpu_to_le16(smb_buffer->Command);
+-      cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
+-      /* easier to use jiffies */
+-      /* when mid allocated can be before when sent */
+-      temp->when_alloc = jiffies;
+-      temp->server = server;
+-
+-      /*
+-       * The default is for the mid to be synchronous, so the
+-       * default callback just wakes up the current task.
+-       */
+-      get_task_struct(current);
+-      temp->creator = current;
+-      temp->callback = cifs_wake_up_task;
+-      temp->callback_data = current;
+-
+-      atomic_inc(&mid_count);
+-      temp->mid_state = MID_REQUEST_ALLOCATED;
+-      return temp;
+-}
+-
+-int
+-smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
+-       unsigned int smb_buf_length)
+-{
+-      struct kvec iov[2];
+-      struct smb_rqst rqst = { .rq_iov = iov,
+-                               .rq_nvec = 2 };
+-
+-      iov[0].iov_base = smb_buffer;
+-      iov[0].iov_len = 4;
+-      iov[1].iov_base = (char *)smb_buffer + 4;
+-      iov[1].iov_len = smb_buf_length;
+-
+-      return __smb_send_rqst(server, 1, &rqst);
+-}
+-
+-static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
+-                      struct mid_q_entry **ppmidQ)
+-{
+-      spin_lock(&ses->ses_lock);
+-      if (ses->ses_status == SES_NEW) {
+-              if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
+-                      (in_buf->Command != SMB_COM_NEGOTIATE)) {
+-                      spin_unlock(&ses->ses_lock);
+-                      return -EAGAIN;
+-              }
+-              /* else ok - we are setting up session */
+-      }
+-
+-      if (ses->ses_status == SES_EXITING) {
+-              /* check if SMB session is bad because we are setting it up */
+-              if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
+-                      spin_unlock(&ses->ses_lock);
+-                      return -EAGAIN;
+-              }
+-              /* else ok - we are shutting down session */
+-      }
+-      spin_unlock(&ses->ses_lock);
+-
+-      *ppmidQ = alloc_mid(in_buf, ses->server);
+-      if (*ppmidQ == NULL)
+-              return -ENOMEM;
+-      spin_lock(&ses->server->mid_queue_lock);
+-      list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
+-      spin_unlock(&ses->server->mid_queue_lock);
+-      return 0;
+-}
+-
+-struct mid_q_entry *
+-cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+-{
+-      int rc;
+-      struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+-      struct mid_q_entry *mid;
+-
+-      if (rqst->rq_iov[0].iov_len != 4 ||
+-          rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
+-              return ERR_PTR(-EIO);
+-
+-      /* enable signing if server requires it */
+-      if (server->sign)
+-              hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+-
+-      mid = alloc_mid(hdr, server);
+-      if (mid == NULL)
+-              return ERR_PTR(-ENOMEM);
+-
+-      rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
+-      if (rc) {
+-              release_mid(mid);
+-              return ERR_PTR(rc);
+-      }
+-
+-      return mid;
+-}
+-
+-/*
+- *
+- * Send an SMB Request.  No response info (other than return code)
+- * needs to be parsed.
+- *
+- * flags indicate the type of request buffer and how long to wait
+- * and whether to log NT STATUS code (error) before mapping it to POSIX error
+- *
+- */
+-int
+-SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
+-               char *in_buf, int flags)
+-{
+-      int rc;
+-      struct kvec iov[1];
+-      struct kvec rsp_iov;
+-      int resp_buf_type;
+-
+-      iov[0].iov_base = in_buf;
+-      iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
+-      flags |= CIFS_NO_RSP_BUF;
+-      rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
+-      cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
+-
+-      return rc;
+-}
+-
+-int
+-cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
+-                 bool log_error)
+-{
+-      unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
+-
+-      dump_smb(mid->resp_buf, min_t(u32, 92, len));
+-
+-      /* convert the length into a more usable form */
+-      if (server->sign) {
+-              struct kvec iov[2];
+-              int rc = 0;
+-              struct smb_rqst rqst = { .rq_iov = iov,
+-                                       .rq_nvec = 2 };
+-
+-              iov[0].iov_base = mid->resp_buf;
+-              iov[0].iov_len = 4;
+-              iov[1].iov_base = (char *)mid->resp_buf + 4;
+-              iov[1].iov_len = len - 4;
+-              /* FIXME: add code to kill session */
+-              rc = cifs_verify_signature(&rqst, server,
+-                                         mid->sequence_number);
+-              if (rc)
+-                      cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
+-                               rc);
+-      }
+-
+-      /* BB special case reconnect tid and uid here? */
+-      return map_and_check_smb_error(mid, log_error);
+-}
+-
+-struct mid_q_entry *
+-cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
+-                 struct smb_rqst *rqst)
+-{
+-      int rc;
+-      struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+-      struct mid_q_entry *mid;
+-
+-      if (rqst->rq_iov[0].iov_len != 4 ||
+-          rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
+-              return ERR_PTR(-EIO);
+-
+-      rc = allocate_mid(ses, hdr, &mid);
+-      if (rc)
+-              return ERR_PTR(rc);
+-      rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
+-      if (rc) {
+-              delete_mid(mid);
+-              return ERR_PTR(rc);
+-      }
+-      return mid;
+-}
+-
+-int
+-SendReceive2(const unsigned int xid, struct cifs_ses *ses,
+-           struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
+-           const int flags, struct kvec *resp_iov)
+-{
+-      struct smb_rqst rqst;
+-      struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
+-      int rc;
+-
+-      if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
+-              new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
+-                                      GFP_KERNEL);
+-              if (!new_iov) {
+-                      /* otherwise cifs_send_recv below sets resp_buf_type */
+-                      *resp_buf_type = CIFS_NO_BUFFER;
+-                      return -ENOMEM;
+-              }
+-      } else
+-              new_iov = s_iov;
+-
+-      /* 1st iov is a RFC1001 length followed by the rest of the packet */
+-      memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
+-
+-      new_iov[0].iov_base = new_iov[1].iov_base;
+-      new_iov[0].iov_len = 4;
+-      new_iov[1].iov_base += 4;
+-      new_iov[1].iov_len -= 4;
+-
+-      memset(&rqst, 0, sizeof(struct smb_rqst));
+-      rqst.rq_iov = new_iov;
+-      rqst.rq_nvec = n_vec + 1;
+-
+-      rc = cifs_send_recv(xid, ses, ses->server,
+-                          &rqst, resp_buf_type, flags, resp_iov);
+-      if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
+-              kfree(new_iov);
+-      return rc;
+-}
+-
+-int
+-SendReceive(const unsigned int xid, struct cifs_ses *ses,
+-          struct smb_hdr *in_buf, struct smb_hdr *out_buf,
+-          int *pbytes_returned, const int flags)
+-{
+-      int rc = 0;
+-      struct mid_q_entry *midQ;
+-      unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
+-      struct kvec iov = { .iov_base = in_buf, .iov_len = len };
+-      struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
+-      struct cifs_credits credits = { .value = 1, .instance = 0 };
+-      struct TCP_Server_Info *server;
+-
+-      if (ses == NULL) {
+-              cifs_dbg(VFS, "Null smb session\n");
+-              return -EIO;
+-      }
+-      server = ses->server;
+-      if (server == NULL) {
+-              cifs_dbg(VFS, "Null tcp session\n");
+-              return -EIO;
+-      }
+-
+-      spin_lock(&server->srv_lock);
+-      if (server->tcpStatus == CifsExiting) {
+-              spin_unlock(&server->srv_lock);
+-              return -ENOENT;
+-      }
+-      spin_unlock(&server->srv_lock);
+-
+-      /* Ensure that we do not send more than 50 overlapping requests
+-         to the same server. We may make this configurable later or
+-         use ses->maxReq */
+-
+-      if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+-              cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
+-                              len);
+-              return -EIO;
+-      }
+-
+-      rc = wait_for_free_request(server, flags, &credits.instance);
+-      if (rc)
+-              return rc;
+-
+-      /* make sure that we sign in the same order that we send on this socket
+-         and avoid races inside tcp sendmsg code that could cause corruption
+-         of smb data */
+-
+-      cifs_server_lock(server);
+-
+-      rc = allocate_mid(ses, in_buf, &midQ);
+-      if (rc) {
+-              cifs_server_unlock(server);
+-              /* Update # of requests on wire to server */
+-              add_credits(server, &credits, 0);
+-              return rc;
+-      }
+-
+-      rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
+-      if (rc) {
+-              cifs_server_unlock(server);
+-              goto out;
+-      }
+-
+-      midQ->mid_state = MID_REQUEST_SUBMITTED;
+-
+-      rc = smb_send(server, in_buf, len);
+-      cifs_save_when_sent(midQ);
+-
+-      if (rc < 0)
+-              server->sequence_number -= 2;
+-
+-      cifs_server_unlock(server);
+-
+-      if (rc < 0)
+-              goto out;
+-
+-      rc = wait_for_response(server, midQ);
+-      if (rc != 0) {
+-              send_cancel(server, &rqst, midQ);
+-              spin_lock(&midQ->mid_lock);
+-              if (midQ->callback) {
+-                      /* no longer considered to be "in-flight" */
+-                      midQ->callback = release_mid;
+-                      spin_unlock(&midQ->mid_lock);
+-                      add_credits(server, &credits, 0);
+-                      return rc;
+-              }
+-              spin_unlock(&midQ->mid_lock);
+-      }
+-
+-      rc = cifs_sync_mid_result(midQ, server);
+-      if (rc != 0) {
+-              add_credits(server, &credits, 0);
+-              return rc;
+-      }
+-
+-      if (!midQ->resp_buf || !out_buf ||
+-          midQ->mid_state != MID_RESPONSE_READY) {
+-              rc = -EIO;
+-              cifs_server_dbg(VFS, "Bad MID state?\n");
+-              goto out;
+-      }
+-
+-      *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
+-      memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
+-      rc = cifs_check_receive(midQ, server, 0);
+-out:
+-      delete_mid(midQ);
+-      add_credits(server, &credits, 0);
+-
+-      return rc;
+-}
+-
+-/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
+-   blocking lock to return. */
+-
+-static int
+-send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
+-                      struct smb_hdr *in_buf,
+-                      struct smb_hdr *out_buf)
+-{
+-      int bytes_returned;
+-      struct cifs_ses *ses = tcon->ses;
+-      LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
+-
+-      /* We just modify the current in_buf to change
+-         the type of lock from LOCKING_ANDX_SHARED_LOCK
+-         or LOCKING_ANDX_EXCLUSIVE_LOCK to
+-         LOCKING_ANDX_CANCEL_LOCK. */
+-
+-      pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
+-      pSMB->Timeout = 0;
+-      pSMB->hdr.Mid = get_next_mid(ses->server);
+-
+-      return SendReceive(xid, ses, in_buf, out_buf,
+-                      &bytes_returned, 0);
+-}
+-
+-int
+-SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
+-          struct smb_hdr *in_buf, struct smb_hdr *out_buf,
+-          int *pbytes_returned)
+-{
+-      int rc = 0;
+-      int rstart = 0;
+-      struct mid_q_entry *midQ;
+-      struct cifs_ses *ses;
+-      unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
+-      struct kvec iov = { .iov_base = in_buf, .iov_len = len };
+-      struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
+-      unsigned int instance;
+-      struct TCP_Server_Info *server;
+-
+-      if (tcon == NULL || tcon->ses == NULL) {
+-              cifs_dbg(VFS, "Null smb session\n");
+-              return -EIO;
+-      }
+-      ses = tcon->ses;
+-      server = ses->server;
+-
+-      if (server == NULL) {
+-              cifs_dbg(VFS, "Null tcp session\n");
+-              return -EIO;
+-      }
+-
+-      spin_lock(&server->srv_lock);
+-      if (server->tcpStatus == CifsExiting) {
+-              spin_unlock(&server->srv_lock);
+-              return -ENOENT;
+-      }
+-      spin_unlock(&server->srv_lock);
+-
+-      /* Ensure that we do not send more than 50 overlapping requests
+-         to the same server. We may make this configurable later or
+-         use ses->maxReq */
+-
+-      if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
+-              cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
+-                            len);
+-              return -EIO;
+-      }
+-
+-      rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
+-      if (rc)
+-              return rc;
+-
+-      /* make sure that we sign in the same order that we send on this socket
+-         and avoid races inside tcp sendmsg code that could cause corruption
+-         of smb data */
+-
+-      cifs_server_lock(server);
+-
+-      rc = allocate_mid(ses, in_buf, &midQ);
+-      if (rc) {
+-              cifs_server_unlock(server);
+-              return rc;
+-      }
+-
+-      rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
+-      if (rc) {
+-              delete_mid(midQ);
+-              cifs_server_unlock(server);
+-              return rc;
+-      }
+-
+-      midQ->mid_state = MID_REQUEST_SUBMITTED;
+-      rc = smb_send(server, in_buf, len);
+-      cifs_save_when_sent(midQ);
+-
+-      if (rc < 0)
+-              server->sequence_number -= 2;
+-
+-      cifs_server_unlock(server);
+-
+-      if (rc < 0) {
+-              delete_mid(midQ);
+-              return rc;
+-      }
+-
+-      /* Wait for a reply - allow signals to interrupt. */
+-      rc = wait_event_interruptible(server->response_q,
+-              (!(midQ->mid_state == MID_REQUEST_SUBMITTED ||
+-                 midQ->mid_state == MID_RESPONSE_RECEIVED)) ||
+-              ((server->tcpStatus != CifsGood) &&
+-               (server->tcpStatus != CifsNew)));
+-
+-      /* Were we interrupted by a signal ? */
+-      spin_lock(&server->srv_lock);
+-      if ((rc == -ERESTARTSYS) &&
+-              (midQ->mid_state == MID_REQUEST_SUBMITTED ||
+-               midQ->mid_state == MID_RESPONSE_RECEIVED) &&
+-              ((server->tcpStatus == CifsGood) ||
+-               (server->tcpStatus == CifsNew))) {
+-              spin_unlock(&server->srv_lock);
+-
+-              if (in_buf->Command == SMB_COM_TRANSACTION2) {
+-                      /* POSIX lock. We send a NT_CANCEL SMB to cause the
+-                         blocking lock to return. */
+-                      rc = send_cancel(server, &rqst, midQ);
+-                      if (rc) {
+-                              delete_mid(midQ);
+-                              return rc;
+-                      }
+-              } else {
+-                      /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
+-                         to cause the blocking lock to return. */
+-
+-                      rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
+-
+-                      /* If we get -ENOLCK back the lock may have
+-                         already been removed. Don't exit in this case. */
+-                      if (rc && rc != -ENOLCK) {
+-                              delete_mid(midQ);
+-                              return rc;
+-                      }
+-              }
+-
+-              rc = wait_for_response(server, midQ);
+-              if (rc) {
+-                      send_cancel(server, &rqst, midQ);
+-                      spin_lock(&midQ->mid_lock);
+-                      if (midQ->callback) {
+-                              /* no longer considered to be "in-flight" */
+-                              midQ->callback = release_mid;
+-                              spin_unlock(&midQ->mid_lock);
+-                              return rc;
+-                      }
+-                      spin_unlock(&midQ->mid_lock);
+-              }
+-
+-              /* We got the response - restart system call. */
+-              rstart = 1;
+-              spin_lock(&server->srv_lock);
+-      }
+-      spin_unlock(&server->srv_lock);
+-
+-      rc = cifs_sync_mid_result(midQ, server);
+-      if (rc != 0)
+-              return rc;
+-
+-      /* rcvd frame is ok */
+-      if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) {
+-              rc = -EIO;
+-              cifs_tcon_dbg(VFS, "Bad MID state?\n");
+-              goto out;
+-      }
+-
+-      *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
+-      memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
+-      rc = cifs_check_receive(midQ, server, 0);
+-out:
+-      delete_mid(midQ);
+-      if (rstart && rc == -EACCES)
+-              return -ERESTARTSYS;
+-      return rc;
+-}
+--- /dev/null
++++ b/fs/smb/client/smb1transport.c
+@@ -0,0 +1,565 @@
++// SPDX-License-Identifier: LGPL-2.1
++/*
++ *
++ *   Copyright (C) International Business Machines  Corp., 2002,2008
++ *   Author(s): Steve French (sfrench@us.ibm.com)
++ *   Jeremy Allison (jra@samba.org) 2006.
++ *
++ */
++
++#include <linux/fs.h>
++#include <linux/list.h>
++#include <linux/gfp.h>
++#include <linux/wait.h>
++#include <linux/net.h>
++#include <linux/delay.h>
++#include <linux/freezer.h>
++#include <linux/tcp.h>
++#include <linux/bvec.h>
++#include <linux/highmem.h>
++#include <linux/uaccess.h>
++#include <linux/processor.h>
++#include <linux/mempool.h>
++#include <linux/sched/signal.h>
++#include <linux/task_io_accounting_ops.h>
++#include "cifspdu.h"
++#include "cifsglob.h"
++#include "cifsproto.h"
++#include "cifs_debug.h"
++#include "smb2proto.h"
++#include "smbdirect.h"
++#include "compress.h"
++
++/* Max number of iovectors we can use off the stack when sending requests. */
++#define CIFS_MAX_IOV_SIZE 8
++
++static struct mid_q_entry *
++alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
++{
++      struct mid_q_entry *temp;
++
++      if (server == NULL) {
++              cifs_dbg(VFS, "%s: null TCP session\n", __func__);
++              return NULL;
++      }
++
++      temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
++      memset(temp, 0, sizeof(struct mid_q_entry));
++      kref_init(&temp->refcount);
++      spin_lock_init(&temp->mid_lock);
++      temp->mid = get_mid(smb_buffer);
++      temp->pid = current->pid;
++      temp->command = cpu_to_le16(smb_buffer->Command);
++      cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command);
++      /* easier to use jiffies */
++      /* when mid allocated can be before when sent */
++      temp->when_alloc = jiffies;
++      temp->server = server;
++
++      /*
++       * The default is for the mid to be synchronous, so the
++       * default callback just wakes up the current task.
++       */
++      get_task_struct(current);
++      temp->creator = current;
++      temp->callback = cifs_wake_up_task;
++      temp->callback_data = current;
++
++      atomic_inc(&mid_count);
++      temp->mid_state = MID_REQUEST_ALLOCATED;
++      return temp;
++}
++
++int
++smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
++       unsigned int smb_buf_length)
++{
++      struct kvec iov[2];
++      struct smb_rqst rqst = { .rq_iov = iov,
++                               .rq_nvec = 2 };
++
++      iov[0].iov_base = smb_buffer;
++      iov[0].iov_len = 4;
++      iov[1].iov_base = (char *)smb_buffer + 4;
++      iov[1].iov_len = smb_buf_length;
++
++      return __smb_send_rqst(server, 1, &rqst);
++}
++
++static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
++                      struct mid_q_entry **ppmidQ)
++{
++      spin_lock(&ses->ses_lock);
++      if (ses->ses_status == SES_NEW) {
++              if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
++                      (in_buf->Command != SMB_COM_NEGOTIATE)) {
++                      spin_unlock(&ses->ses_lock);
++                      return -EAGAIN;
++              }
++              /* else ok - we are setting up session */
++      }
++
++      if (ses->ses_status == SES_EXITING) {
++              /* check if SMB session is bad because we are setting it up */
++              if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
++                      spin_unlock(&ses->ses_lock);
++                      return -EAGAIN;
++              }
++              /* else ok - we are shutting down session */
++      }
++      spin_unlock(&ses->ses_lock);
++
++      *ppmidQ = alloc_mid(in_buf, ses->server);
++      if (*ppmidQ == NULL)
++              return -ENOMEM;
++      spin_lock(&ses->server->mid_queue_lock);
++      list_add_tail(&(*ppmidQ)->qhead, &ses->server->pending_mid_q);
++      spin_unlock(&ses->server->mid_queue_lock);
++      return 0;
++}
++
++struct mid_q_entry *
++cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
++{
++      int rc;
++      struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
++      struct mid_q_entry *mid;
++
++      if (rqst->rq_iov[0].iov_len != 4 ||
++          rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
++              return ERR_PTR(-EIO);
++
++      /* enable signing if server requires it */
++      if (server->sign)
++              hdr->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
++
++      mid = alloc_mid(hdr, server);
++      if (mid == NULL)
++              return ERR_PTR(-ENOMEM);
++
++      rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
++      if (rc) {
++              release_mid(mid);
++              return ERR_PTR(rc);
++      }
++
++      return mid;
++}
++
++/*
++ *
++ * Send an SMB Request.  No response info (other than return code)
++ * needs to be parsed.
++ *
++ * flags indicate the type of request buffer and how long to wait
++ * and whether to log NT STATUS code (error) before mapping it to POSIX error
++ *
++ */
++int
++SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
++               char *in_buf, int flags)
++{
++      int rc;
++      struct kvec iov[1];
++      struct kvec rsp_iov;
++      int resp_buf_type;
++
++      iov[0].iov_base = in_buf;
++      iov[0].iov_len = get_rfc1002_length(in_buf) + 4;
++      flags |= CIFS_NO_RSP_BUF;
++      rc = SendReceive2(xid, ses, iov, 1, &resp_buf_type, flags, &rsp_iov);
++      cifs_dbg(NOISY, "SendRcvNoRsp flags %d rc %d\n", flags, rc);
++
++      return rc;
++}
++
++int
++cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
++                 bool log_error)
++{
++      unsigned int len = get_rfc1002_length(mid->resp_buf) + 4;
++
++      dump_smb(mid->resp_buf, min_t(u32, 92, len));
++
++      /* convert the length into a more usable form */
++      if (server->sign) {
++              struct kvec iov[2];
++              int rc = 0;
++              struct smb_rqst rqst = { .rq_iov = iov,
++                                       .rq_nvec = 2 };
++
++              iov[0].iov_base = mid->resp_buf;
++              iov[0].iov_len = 4;
++              iov[1].iov_base = (char *)mid->resp_buf + 4;
++              iov[1].iov_len = len - 4;
++              /* FIXME: add code to kill session */
++              rc = cifs_verify_signature(&rqst, server,
++                                         mid->sequence_number);
++              if (rc)
++                      cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
++                               rc);
++      }
++
++      /* BB special case reconnect tid and uid here? */
++      return map_and_check_smb_error(mid, log_error);
++}
++
++struct mid_q_entry *
++cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
++                 struct smb_rqst *rqst)
++{
++      int rc;
++      struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
++      struct mid_q_entry *mid;
++
++      if (rqst->rq_iov[0].iov_len != 4 ||
++          rqst->rq_iov[0].iov_base + 4 != rqst->rq_iov[1].iov_base)
++              return ERR_PTR(-EIO);
++
++      rc = allocate_mid(ses, hdr, &mid);
++      if (rc)
++              return ERR_PTR(rc);
++      rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
++      if (rc) {
++              delete_mid(mid);
++              return ERR_PTR(rc);
++      }
++      return mid;
++}
++
++int
++SendReceive2(const unsigned int xid, struct cifs_ses *ses,
++           struct kvec *iov, int n_vec, int *resp_buf_type /* ret */,
++           const int flags, struct kvec *resp_iov)
++{
++      struct smb_rqst rqst;
++      struct kvec s_iov[CIFS_MAX_IOV_SIZE], *new_iov;
++      int rc;
++
++      if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
++              new_iov = kmalloc_array(n_vec + 1, sizeof(struct kvec),
++                                      GFP_KERNEL);
++              if (!new_iov) {
++                      /* otherwise cifs_send_recv below sets resp_buf_type */
++                      *resp_buf_type = CIFS_NO_BUFFER;
++                      return -ENOMEM;
++              }
++      } else
++              new_iov = s_iov;
++
++      /* 1st iov is a RFC1001 length followed by the rest of the packet */
++      memcpy(new_iov + 1, iov, (sizeof(struct kvec) * n_vec));
++
++      new_iov[0].iov_base = new_iov[1].iov_base;
++      new_iov[0].iov_len = 4;
++      new_iov[1].iov_base += 4;
++      new_iov[1].iov_len -= 4;
++
++      memset(&rqst, 0, sizeof(struct smb_rqst));
++      rqst.rq_iov = new_iov;
++      rqst.rq_nvec = n_vec + 1;
++
++      rc = cifs_send_recv(xid, ses, ses->server,
++                          &rqst, resp_buf_type, flags, resp_iov);
++      if (n_vec + 1 > CIFS_MAX_IOV_SIZE)
++              kfree(new_iov);
++      return rc;
++}
++
++int
++SendReceive(const unsigned int xid, struct cifs_ses *ses,
++          struct smb_hdr *in_buf, struct smb_hdr *out_buf,
++          int *pbytes_returned, const int flags)
++{
++      int rc = 0;
++      struct mid_q_entry *midQ;
++      unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
++      struct kvec iov = { .iov_base = in_buf, .iov_len = len };
++      struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
++      struct cifs_credits credits = { .value = 1, .instance = 0 };
++      struct TCP_Server_Info *server;
++
++      if (ses == NULL) {
++              cifs_dbg(VFS, "Null smb session\n");
++              return -EIO;
++      }
++      server = ses->server;
++      if (server == NULL) {
++              cifs_dbg(VFS, "Null tcp session\n");
++              return -EIO;
++      }
++
++      spin_lock(&server->srv_lock);
++      if (server->tcpStatus == CifsExiting) {
++              spin_unlock(&server->srv_lock);
++              return -ENOENT;
++      }
++      spin_unlock(&server->srv_lock);
++
++      /* Ensure that we do not send more than 50 overlapping requests
++         to the same server. We may make this configurable later or
++         use ses->maxReq */
++
++      if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
++              cifs_server_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
++                              len);
++              return -EIO;
++      }
++
++      rc = wait_for_free_request(server, flags, &credits.instance);
++      if (rc)
++              return rc;
++
++      /* make sure that we sign in the same order that we send on this socket
++         and avoid races inside tcp sendmsg code that could cause corruption
++         of smb data */
++
++      cifs_server_lock(server);
++
++      rc = allocate_mid(ses, in_buf, &midQ);
++      if (rc) {
++              cifs_server_unlock(server);
++              /* Update # of requests on wire to server */
++              add_credits(server, &credits, 0);
++              return rc;
++      }
++
++      rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
++      if (rc) {
++              cifs_server_unlock(server);
++              goto out;
++      }
++
++      midQ->mid_state = MID_REQUEST_SUBMITTED;
++
++      rc = smb_send(server, in_buf, len);
++      cifs_save_when_sent(midQ);
++
++      if (rc < 0)
++              server->sequence_number -= 2;
++
++      cifs_server_unlock(server);
++
++      if (rc < 0)
++              goto out;
++
++      rc = wait_for_response(server, midQ);
++      if (rc != 0) {
++              send_cancel(server, &rqst, midQ);
++              spin_lock(&midQ->mid_lock);
++              if (midQ->callback) {
++                      /* no longer considered to be "in-flight" */
++                      midQ->callback = release_mid;
++                      spin_unlock(&midQ->mid_lock);
++                      add_credits(server, &credits, 0);
++                      return rc;
++              }
++              spin_unlock(&midQ->mid_lock);
++      }
++
++      rc = cifs_sync_mid_result(midQ, server);
++      if (rc != 0) {
++              add_credits(server, &credits, 0);
++              return rc;
++      }
++
++      if (!midQ->resp_buf || !out_buf ||
++          midQ->mid_state != MID_RESPONSE_READY) {
++              rc = -EIO;
++              cifs_server_dbg(VFS, "Bad MID state?\n");
++              goto out;
++      }
++
++      *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
++      memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
++      rc = cifs_check_receive(midQ, server, 0);
++out:
++      delete_mid(midQ);
++      add_credits(server, &credits, 0);
++
++      return rc;
++}
++
++/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
++   blocking lock to return. */
++
++static int
++send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
++                      struct smb_hdr *in_buf,
++                      struct smb_hdr *out_buf)
++{
++      int bytes_returned;
++      struct cifs_ses *ses = tcon->ses;
++      LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
++
++      /* We just modify the current in_buf to change
++         the type of lock from LOCKING_ANDX_SHARED_LOCK
++         or LOCKING_ANDX_EXCLUSIVE_LOCK to
++         LOCKING_ANDX_CANCEL_LOCK. */
++
++      pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
++      pSMB->Timeout = 0;
++      pSMB->hdr.Mid = get_next_mid(ses->server);
++
++      return SendReceive(xid, ses, in_buf, out_buf,
++                      &bytes_returned, 0);
++}
++
++int
++SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
++          struct smb_hdr *in_buf, struct smb_hdr *out_buf,
++          int *pbytes_returned)
++{
++      int rc = 0;
++      int rstart = 0;
++      struct mid_q_entry *midQ;
++      struct cifs_ses *ses;
++      unsigned int len = be32_to_cpu(in_buf->smb_buf_length);
++      struct kvec iov = { .iov_base = in_buf, .iov_len = len };
++      struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
++      unsigned int instance;
++      struct TCP_Server_Info *server;
++
++      if (tcon == NULL || tcon->ses == NULL) {
++              cifs_dbg(VFS, "Null smb session\n");
++              return -EIO;
++      }
++      ses = tcon->ses;
++      server = ses->server;
++
++      if (server == NULL) {
++              cifs_dbg(VFS, "Null tcp session\n");
++              return -EIO;
++      }
++
++      spin_lock(&server->srv_lock);
++      if (server->tcpStatus == CifsExiting) {
++              spin_unlock(&server->srv_lock);
++              return -ENOENT;
++      }
++      spin_unlock(&server->srv_lock);
++
++      /* Ensure that we do not send more than 50 overlapping requests
++         to the same server. We may make this configurable later or
++         use ses->maxReq */
++
++      if (len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
++              cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
++                            len);
++              return -EIO;
++      }
++
++      rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
++      if (rc)
++              return rc;
++
++      /* make sure that we sign in the same order that we send on this socket
++         and avoid races inside tcp sendmsg code that could cause corruption
++         of smb data */
++
++      cifs_server_lock(server);
++
++      rc = allocate_mid(ses, in_buf, &midQ);
++      if (rc) {
++              cifs_server_unlock(server);
++              return rc;
++      }
++
++      rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
++      if (rc) {
++              delete_mid(midQ);
++              cifs_server_unlock(server);
++              return rc;
++      }
++
++      midQ->mid_state = MID_REQUEST_SUBMITTED;
++      rc = smb_send(server, in_buf, len);
++      cifs_save_when_sent(midQ);
++
++      if (rc < 0)
++              server->sequence_number -= 2;
++
++      cifs_server_unlock(server);
++
++      if (rc < 0) {
++              delete_mid(midQ);
++              return rc;
++      }
++
++      /* Wait for a reply - allow signals to interrupt. */
++      rc = wait_event_interruptible(server->response_q,
++              (!(midQ->mid_state == MID_REQUEST_SUBMITTED ||
++                 midQ->mid_state == MID_RESPONSE_RECEIVED)) ||
++              ((server->tcpStatus != CifsGood) &&
++               (server->tcpStatus != CifsNew)));
++
++      /* Were we interrupted by a signal ? */
++      spin_lock(&server->srv_lock);
++      if ((rc == -ERESTARTSYS) &&
++              (midQ->mid_state == MID_REQUEST_SUBMITTED ||
++               midQ->mid_state == MID_RESPONSE_RECEIVED) &&
++              ((server->tcpStatus == CifsGood) ||
++               (server->tcpStatus == CifsNew))) {
++              spin_unlock(&server->srv_lock);
++
++              if (in_buf->Command == SMB_COM_TRANSACTION2) {
++                      /* POSIX lock. We send a NT_CANCEL SMB to cause the
++                         blocking lock to return. */
++                      rc = send_cancel(server, &rqst, midQ);
++                      if (rc) {
++                              delete_mid(midQ);
++                              return rc;
++                      }
++              } else {
++                      /* Windows lock. We send a LOCKINGX_CANCEL_LOCK
++                         to cause the blocking lock to return. */
++
++                      rc = send_lock_cancel(xid, tcon, in_buf, out_buf);
++
++                      /* If we get -ENOLCK back the lock may have
++                         already been removed. Don't exit in this case. */
++                      if (rc && rc != -ENOLCK) {
++                              delete_mid(midQ);
++                              return rc;
++                      }
++              }
++
++              rc = wait_for_response(server, midQ);
++              if (rc) {
++                      send_cancel(server, &rqst, midQ);
++                      spin_lock(&midQ->mid_lock);
++                      if (midQ->callback) {
++                              /* no longer considered to be "in-flight" */
++                              midQ->callback = release_mid;
++                              spin_unlock(&midQ->mid_lock);
++                              return rc;
++                      }
++                      spin_unlock(&midQ->mid_lock);
++              }
++
++              /* We got the response - restart system call. */
++              rstart = 1;
++              spin_lock(&server->srv_lock);
++      }
++      spin_unlock(&server->srv_lock);
++
++      rc = cifs_sync_mid_result(midQ, server);
++      if (rc != 0)
++              return rc;
++
++      /* rcvd frame is ok */
++      if (out_buf == NULL || midQ->mid_state != MID_RESPONSE_READY) {
++              rc = -EIO;
++              cifs_tcon_dbg(VFS, "Bad MID state?\n");
++              goto out;
++      }
++
++      *pbytes_returned = get_rfc1002_length(midQ->resp_buf);
++      memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
++      rc = cifs_check_receive(midQ, server, 0);
++out:
++      delete_mid(midQ);
++      if (rstart && rc == -EACCES)
++              return -ERESTARTSYS;
++      return rc;
++}
diff --git a/queue-6.18/crypto-atmel-sha204a-fail-on-hwrng-registration-error-in-probe-path.patch b/queue-6.18/crypto-atmel-sha204a-fail-on-hwrng-registration-error-in-probe-path.patch
new file mode 100644 (file)
index 0000000..ec10cb1
--- /dev/null
@@ -0,0 +1,55 @@
+From stable+bounces-277786-greg=kroah.com@vger.kernel.org Mon Jul 20 16:15:18 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 09:30:39 -0400
+Subject: crypto: atmel-sha204a - fail on hwrng registration error in probe path
+To: stable@vger.kernel.org
+Cc: Thorsten Blum <thorsten.blum@linux.dev>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720133039.1149543-1-sashal@kernel.org>
+
+From: Thorsten Blum <thorsten.blum@linux.dev>
+
+[ Upstream commit 49e05bb00f2e8168695f7af4d694c39e1423e8a2 ]
+
+Commit 13909a0c8897 ("crypto: atmel-sha204a - provide the otp content")
+overwrote the hwrng registration return value when creating the sysfs
+group, which allowed atmel_sha204a_probe() to succeed even if
+devm_hwrng_register() failed.
+
+Return immediately when devm_hwrng_register() fails, and report both
+hwrng and sysfs registration errors with dev_err(). Adjust the sysfs
+error log message for consistency.
+
+Fixes: 13909a0c8897 ("crypto: atmel-sha204a - provide the otp content")
+Cc: stable@vger.kernel.org
+Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/atmel-sha204a.c |    8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/drivers/crypto/atmel-sha204a.c
++++ b/drivers/crypto/atmel-sha204a.c
+@@ -183,8 +183,10 @@ static int atmel_sha204a_probe(struct i2
+               i2c_priv->hwrng.quality = *quality;
+       ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng);
+-      if (ret)
+-              dev_warn(&client->dev, "failed to register RNG (%d)\n", ret);
++      if (ret) {
++              dev_err(&client->dev, "failed to register RNG (%d)\n", ret);
++              return ret;
++      }
+       /* otp read out */
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+@@ -192,7 +194,7 @@ static int atmel_sha204a_probe(struct i2
+       ret = sysfs_create_group(&client->dev.kobj, &atmel_sha204a_groups);
+       if (ret) {
+-              dev_err(&client->dev, "failed to register sysfs entry\n");
++              dev_err(&client->dev, "failed to create sysfs group (%d)\n", ret);
+               return ret;
+       }
diff --git a/queue-6.18/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch b/queue-6.18/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch
new file mode 100644 (file)
index 0000000..d24e488
--- /dev/null
@@ -0,0 +1,57 @@
+From stable+bounces-278178-greg=kroah.com@vger.kernel.org Mon Jul 20 20:12:43 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 14:08:35 -0400
+Subject: crypto: qat - fix restarting state leak on allocation failure
+To: stable@vger.kernel.org
+Cc: Ahsan Atta <ahsan.atta@intel.com>, Maksim Lukoshkov <maksim.lukoshkov@intel.com>, Giovanni Cabiddu <giovanni.cabiddu@intel.com>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720180835.2922413-1-sashal@kernel.org>
+
+From: Ahsan Atta <ahsan.atta@intel.com>
+
+[ Upstream commit 7d3ed20f7e46b3e991936fedd7a28f3ff4aec8d2 ]
+
+In adf_dev_aer_schedule_reset(), ADF_STATUS_RESTARTING is set before
+allocating reset_data. If the allocation fails, the function returns
+-ENOMEM without queuing reset work, so nothing ever clears the bit.
+This leaves the device permanently stuck in the restarting state,
+causing all subsequent reset attempts to be silently skipped.
+
+Fix this by using test_and_set_bit() to atomically claim the
+RESTARTING state, preventing duplicate reset scheduling races under
+concurrent fatal error reporting. If the subsequent allocation fails,
+clear the bit to restore clean state so future reset attempts can
+proceed.
+
+Cc: stable@vger.kernel.org
+Fixes: d8cba25d2c68 ("crypto: qat - Intel(R) QAT driver framework")
+Signed-off-by: Ahsan Atta <ahsan.atta@intel.com>
+Co-developed-by: Maksim Lukoshkov <maksim.lukoshkov@intel.com>
+Signed-off-by: Maksim Lukoshkov <maksim.lukoshkov@intel.com>
+Reviewed-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/intel/qat/qat_common/adf_aer.c |    7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/crypto/intel/qat/qat_common/adf_aer.c
++++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c
+@@ -207,13 +207,14 @@ static int adf_dev_aer_schedule_reset(st
+       struct adf_reset_dev_data *reset_data;
+       if (!adf_dev_started(accel_dev) ||
+-          test_bit(ADF_STATUS_RESTARTING, &accel_dev->status))
++          test_and_set_bit(ADF_STATUS_RESTARTING, &accel_dev->status))
+               return 0;
+-      set_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
+       reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
+-      if (!reset_data)
++      if (!reset_data) {
++              clear_bit(ADF_STATUS_RESTARTING, &accel_dev->status);
+               return -ENOMEM;
++      }
+       reset_data->accel_dev = accel_dev;
+       init_completion(&reset_data->compl);
+       reset_data->mode = mode;
diff --git a/queue-6.18/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch b/queue-6.18/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch
new file mode 100644 (file)
index 0000000..2d015dd
--- /dev/null
@@ -0,0 +1,218 @@
+From stable+bounces-274652-greg=kroah.com@vger.kernel.org Wed Jul 15 02:47:26 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 20:47:11 -0400
+Subject: crypto: qat - fix VF2PF work teardown race in adf_disable_sriov()
+To: stable@vger.kernel.org
+Cc: Giovanni Cabiddu <giovanni.cabiddu@intel.com>, Ahsan Atta <ahsan.atta@intel.com>, Herbert Xu <herbert@gondor.apana.org.au>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715004711.3934675-1-sashal@kernel.org>
+
+From: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+
+[ Upstream commit 277281c10c63791067d24d421f7c43a15faa9096 ]
+
+The VF2PF interrupt handler queues PF-side response work that stores a
+raw pointer to per-VF state (struct adf_accel_vf_info). Currently,
+adf_disable_sriov() destroys per-VF mutexes and frees vf_info without
+stopping new VF2PF work or waiting for in-flight workers to complete. A
+concurrently scheduled or already queued worker can then dereference
+freed memory.
+
+This manifests as a use-after-free when KASAN is enabled:
+
+  BUG: KASAN: null-ptr-deref in mutex_lock+0x76/0xe0
+  Write of size 8 at addr 0000000000000260 by task kworker/24:2/...
+  Workqueue: qat_pf2vf_resp_wq adf_iov_send_resp [intel_qat]
+  Call Trace:
+    kasan_report+0x119/0x140
+    mutex_lock+0x76/0xe0
+    adf_gen4_pfvf_send+0xd4/0x1f0 [intel_qat]
+    adf_recv_and_handle_vf2pf_msg+0x290/0x360 [intel_qat]
+    adf_iov_send_resp+0x8c/0xe0 [intel_qat]
+    process_one_work+0x6ac/0xfd0
+    worker_thread+0x4dd/0xd30
+    kthread+0x326/0x410
+    ret_from_fork+0x33b/0x670
+
+Add a PF-local flag, vf2pf_disabled, that gates work queueing, worker
+processing, and interrupt re-enabling during teardown. Set this flag
+atomically with the hardware interrupt mask inside
+adf_disable_all_vf2pf_interrupts(). After masking, synchronize the AE
+cluster MSI-X interrupt and flush the PF response workqueue before
+tearing down per-VF locks and state so all in-flight work completes
+before vf_info is destroyed.
+
+Introduce adf_enable_all_vf2pf_interrupts() to clear the flag and
+unmask all VF2PF interrupts under the same lock when SR-IOV is
+re-enabled. This ensures the software flag and hardware state transition
+atomically on both the enable and disable paths.
+
+Cc: stable@vger.kernel.org
+Fixes: ed8ccaef52fa ("crypto: qat - Add support for SRIOV")
+Signed-off-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
+Reviewed-by: Ahsan Atta <ahsan.atta@intel.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/crypto/intel/qat/qat_common/adf_accel_devices.h |    2 
+ drivers/crypto/intel/qat/qat_common/adf_common_drv.h    |    2 
+ drivers/crypto/intel/qat/qat_common/adf_isr.c           |   39 ++++++++++++++++
+ drivers/crypto/intel/qat/qat_common/adf_sriov.c         |   20 +++++++-
+ 4 files changed, 61 insertions(+), 2 deletions(-)
+
+--- a/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h
++++ b/drivers/crypto/intel/qat/qat_common/adf_accel_devices.h
+@@ -472,6 +472,8 @@ struct adf_accel_dev {
+               struct {
+                       /* protects VF2PF interrupts access */
+                       spinlock_t vf2pf_ints_lock;
++                      /* prevents VF2PF handling from racing with VF state teardown */
++                      bool vf2pf_disabled;
+                       /* vf_info is non-zero when SR-IOV is init'ed */
+                       struct adf_accel_vf_info *vf_info;
+               } pf;
+--- a/drivers/crypto/intel/qat/qat_common/adf_common_drv.h
++++ b/drivers/crypto/intel/qat/qat_common/adf_common_drv.h
+@@ -118,6 +118,7 @@ void qat_comp_alg_callback(void *resp);
+ int adf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
+ void adf_isr_resource_free(struct adf_accel_dev *accel_dev);
++void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev);
+ int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev);
+ void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev);
+@@ -191,6 +192,7 @@ int adf_sriov_configure(struct pci_dev *
+ void adf_disable_sriov(struct adf_accel_dev *accel_dev);
+ void adf_reenable_sriov(struct adf_accel_dev *accel_dev);
+ void adf_enable_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 vf_mask);
++void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs);
+ void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev);
+ bool adf_recv_and_handle_pf2vf_msg(struct adf_accel_dev *accel_dev);
+ bool adf_recv_and_handle_vf2pf_msg(struct adf_accel_dev *accel_dev, u32 vf_nr);
+--- a/drivers/crypto/intel/qat/qat_common/adf_isr.c
++++ b/drivers/crypto/intel/qat/qat_common/adf_isr.c
+@@ -62,6 +62,23 @@ void adf_enable_vf2pf_interrupts(struct
+       unsigned long flags;
+       spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
++      if (!READ_ONCE(accel_dev->pf.vf2pf_disabled))
++              GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask);
++      spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
++}
++
++void adf_enable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev, u32 num_vfs)
++{
++      void __iomem *pmisc_addr = adf_get_pmisc_base(accel_dev);
++      unsigned long flags;
++      u32 vf_mask;
++
++      vf_mask = BIT_ULL(num_vfs) - 1;
++      if (!vf_mask)
++              return;
++
++      spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
++      WRITE_ONCE(accel_dev->pf.vf2pf_disabled, false);
+       GET_PFVF_OPS(accel_dev)->enable_vf2pf_interrupts(pmisc_addr, vf_mask);
+       spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
+ }
+@@ -72,6 +89,7 @@ void adf_disable_all_vf2pf_interrupts(st
+       unsigned long flags;
+       spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags);
++      WRITE_ONCE(accel_dev->pf.vf2pf_disabled, true);
+       GET_PFVF_OPS(accel_dev)->disable_all_vf2pf_interrupts(pmisc_addr);
+       spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags);
+ }
+@@ -174,6 +192,27 @@ static irqreturn_t adf_msix_isr_ae(int i
+       return IRQ_NONE;
+ }
++void adf_isr_sync_ae_cluster(struct adf_accel_dev *accel_dev)
++{
++      struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev;
++      struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
++      u32 num_entries = pci_dev_info->msix_entries.num_entries;
++      struct adf_irq *irqs = pci_dev_info->msix_entries.irqs;
++      u32 irq_idx;
++      int irq;
++
++      if (!test_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status) || !irqs)
++              return;
++
++      irq_idx = num_entries > 1 ? hw_data->num_banks : 0;
++      if (irq_idx >= num_entries || !irqs[irq_idx].enabled)
++              return;
++
++      irq = pci_irq_vector(pci_dev_info->pci_dev, hw_data->num_banks);
++      if (irq > 0)
++              synchronize_irq(irq);
++}
++
+ static void adf_free_irqs(struct adf_accel_dev *accel_dev)
+ {
+       struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev;
+--- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c
++++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c
+@@ -26,6 +26,9 @@ static void adf_iov_send_resp(struct wor
+       u32 vf_nr = vf_info->vf_nr;
+       bool ret;
++      if (READ_ONCE(accel_dev->pf.vf2pf_disabled))
++              goto out;
++
+       mutex_lock(&vf_info->pfvf_mig_lock);
+       ret = adf_recv_and_handle_vf2pf_msg(accel_dev, vf_nr);
+       if (ret)
+@@ -33,13 +36,18 @@ static void adf_iov_send_resp(struct wor
+               adf_enable_vf2pf_interrupts(accel_dev, 1 << vf_nr);
+       mutex_unlock(&vf_info->pfvf_mig_lock);
++out:
+       kfree(pf2vf_resp);
+ }
+ void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info)
+ {
++      struct adf_accel_dev *accel_dev = vf_info->accel_dev;
+       struct adf_pf2vf_resp *pf2vf_resp;
++      if (READ_ONCE(accel_dev->pf.vf2pf_disabled))
++              return;
++
+       pf2vf_resp = kzalloc(sizeof(*pf2vf_resp), GFP_ATOMIC);
+       if (!pf2vf_resp)
+               return;
+@@ -49,6 +57,12 @@ void adf_schedule_vf2pf_handler(struct a
+       queue_work(pf2vf_resp_wq, &pf2vf_resp->pf2vf_resp_work);
+ }
++static void adf_flush_pf2vf_resp_wq(void)
++{
++      if (pf2vf_resp_wq)
++              flush_workqueue(pf2vf_resp_wq);
++}
++
+ static int adf_enable_sriov(struct adf_accel_dev *accel_dev)
+ {
+       struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
+@@ -75,7 +89,7 @@ static int adf_enable_sriov(struct adf_a
+               hw_data->configure_iov_threads(accel_dev, true);
+       /* Enable VF to PF interrupts for all VFs */
+-      adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1);
++      adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs);
+       /* Do not enable SR-IOV if already enabled */
+       if (pci_num_vf(pdev))
+@@ -259,8 +273,10 @@ void adf_disable_sriov(struct adf_accel_
+       if (!test_bit(ADF_STATUS_RESTARTING, &accel_dev->status))
+               pci_disable_sriov(accel_to_pci_dev(accel_dev));
+-      /* Disable VF to PF interrupts */
++      /* Block VF2PF work and disable VF to PF interrupts */
+       adf_disable_all_vf2pf_interrupts(accel_dev);
++      adf_isr_sync_ae_cluster(accel_dev);
++      adf_flush_pf2vf_resp_wq();
+       /* Clear Valid bits in AE Thread to PCIe Function Mapping */
+       if (hw_data->configure_iov_threads)
diff --git a/queue-6.18/firmware_loader-add-cancel-helper-for-async-requests.patch b/queue-6.18/firmware_loader-add-cancel-helper-for-async-requests.patch
new file mode 100644 (file)
index 0000000..8d60080
--- /dev/null
@@ -0,0 +1,180 @@
+From stable+bounces-274117-greg=kroah.com@vger.kernel.org Tue Jul 14 06:05:48 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 00:05:38 -0400
+Subject: firmware_loader: Add cancel helper for async requests
+To: stable@vger.kernel.org
+Cc: "Cássio Gabriel" <cassiogabrielcontato@gmail.com>, "Takashi Iwai" <tiwai@suse.de>, "Danilo Krummrich" <dakr@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714040539.2433035-1-sashal@kernel.org>
+
+From: Cássio Gabriel <cassiogabrielcontato@gmail.com>
+
+[ Upstream commit b9bdd68b8b979c7e9de58b2e7d21e1d7d932c755 ]
+
+request_firmware_nowait() keeps the callback module pinned and holds
+a device reference until the firmware work completes.
+
+Callers still have no way to cancel or synchronize the queued callback
+before tearing down their driver-private state.
+
+Track scheduled async firmware work in an internal list and add
+request_firmware_nowait_cancel(). The helper cancels work matching the
+device, callback context and callback function. It cancels work that has
+not started yet and waits for an already-running callback to return. If
+the request has already completed, it is a no-op.
+
+Keep the existing request_firmware_nowait() lifetime model manual. A
+devres-managed variant can be layered on top separately if needed.
+
+Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
+Reviewed-by: Takashi Iwai <tiwai@suse.de>
+Acked-by: Danilo Krummrich <dakr@kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Link: https://patch.msgid.link/20260505-alsa-hda-tas2781-fw-callback-teardown-v4-1-e7c4bf930dc8@gmail.com
+Stable-dep-of: 5367e2ad14f0 ("ALSA: hda/tas2781: Cancel async firmware request at unbind")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/base/firmware_loader/main.c |   68 +++++++++++++++++++++++++++++++++---
+ include/linux/firmware.h            |   10 +++++
+ 2 files changed, 74 insertions(+), 4 deletions(-)
+
+--- a/drivers/base/firmware_loader/main.c
++++ b/drivers/base/firmware_loader/main.c
+@@ -1141,6 +1141,7 @@ EXPORT_SYMBOL(release_firmware);
+ /* Async support */
+ struct firmware_work {
+       struct work_struct work;
++      struct list_head list;
+       struct module *module;
+       const char *name;
+       struct device *device;
+@@ -1149,6 +1150,17 @@ struct firmware_work {
+       u32 opt_flags;
+ };
++static LIST_HEAD(firmware_work_list);
++static DEFINE_SPINLOCK(firmware_work_lock);
++
++static void firmware_work_free(struct firmware_work *fw_work)
++{
++      put_device(fw_work->device); /* taken in request_firmware_nowait() */
++      module_put(fw_work->module);
++      kfree_const(fw_work->name);
++      kfree(fw_work);
++}
++
+ static void request_firmware_work_func(struct work_struct *work)
+ {
+       struct firmware_work *fw_work;
+@@ -1159,11 +1171,15 @@ static void request_firmware_work_func(s
+       _request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0,
+                         fw_work->opt_flags);
+       fw_work->cont(fw, fw_work->context);
+-      put_device(fw_work->device); /* taken in request_firmware_nowait() */
+-      module_put(fw_work->module);
+-      kfree_const(fw_work->name);
+-      kfree(fw_work);
++      spin_lock_irq(&firmware_work_lock);
++      if (!list_empty(&fw_work->list)) {
++              list_del_init(&fw_work->list);
++              spin_unlock_irq(&firmware_work_lock);
++              firmware_work_free(fw_work);
++              return;
++      }
++      spin_unlock_irq(&firmware_work_lock);
+ }
+@@ -1173,6 +1189,7 @@ static int _request_firmware_nowait(
+       void (*cont)(const struct firmware *fw, void *context), bool nowarn)
+ {
+       struct firmware_work *fw_work;
++      unsigned long flags;
+       fw_work = kzalloc(sizeof(struct firmware_work), gfp);
+       if (!fw_work)
+@@ -1205,7 +1222,12 @@ static int _request_firmware_nowait(
+       get_device(fw_work->device);
+       INIT_WORK(&fw_work->work, request_firmware_work_func);
++
++      spin_lock_irqsave(&firmware_work_lock, flags);
++      list_add_tail(&fw_work->list, &firmware_work_list);
+       schedule_work(&fw_work->work);
++      spin_unlock_irqrestore(&firmware_work_lock, flags);
++
+       return 0;
+ }
+@@ -1268,6 +1290,44 @@ int firmware_request_nowait_nowarn(
+ }
+ EXPORT_SYMBOL_GPL(firmware_request_nowait_nowarn);
++/**
++ * request_firmware_nowait_cancel() - cancel an async firmware request
++ * @device: device for which the firmware is being loaded
++ * @context: context passed to request_firmware_nowait()
++ * @cont: callback passed to request_firmware_nowait()
++ *
++ * Cancel a pending request_firmware_nowait() request for @device, @context
++ * and @cont. If the associated work has already started, this function waits
++ * until the callback has returned. If the callback has already completed, this
++ * function does nothing.
++ *
++ * This function may sleep.
++ */
++void request_firmware_nowait_cancel(struct device *device, void *context,
++                                  void (*cont)(const struct firmware *fw,
++                                               void *context))
++{
++      struct firmware_work *fw_work = NULL;
++      struct firmware_work *tmp;
++
++      spin_lock_irq(&firmware_work_lock);
++      list_for_each_entry_reverse(tmp, &firmware_work_list, list) {
++              if (tmp->device == device && tmp->context == context &&
++                  tmp->cont == cont) {
++                      fw_work = tmp;
++                      list_del_init(&fw_work->list);
++                      break;
++              }
++      }
++      spin_unlock_irq(&firmware_work_lock);
++
++      if (!fw_work)
++              return;
++      cancel_work_sync(&fw_work->work);
++      firmware_work_free(fw_work);
++}
++EXPORT_SYMBOL_GPL(request_firmware_nowait_cancel);
++
+ #ifdef CONFIG_FW_CACHE
+ static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
+--- a/include/linux/firmware.h
++++ b/include/linux/firmware.h
+@@ -110,6 +110,9 @@ int request_firmware_nowait(
+       struct module *module, bool uevent,
+       const char *name, struct device *device, gfp_t gfp, void *context,
+       void (*cont)(const struct firmware *fw, void *context));
++void request_firmware_nowait_cancel(struct device *device, void *context,
++                                  void (*cont)(const struct firmware *fw,
++                                               void *context));
+ int request_firmware_direct(const struct firmware **fw, const char *name,
+                           struct device *device);
+ int request_firmware_into_buf(const struct firmware **firmware_p,
+@@ -157,6 +160,13 @@ static inline int request_firmware_nowai
+       return -EINVAL;
+ }
++static inline void request_firmware_nowait_cancel(struct device *device,
++                                                void *context,
++                                                void (*cont)(const struct firmware *fw,
++                                                             void *context))
++{
++}
++
+ static inline void release_firmware(const struct firmware *fw)
+ {
+ }
diff --git a/queue-6.18/functionfs-don-t-abuse-ffs_data_closed-on-fs-shutdown.patch b/queue-6.18/functionfs-don-t-abuse-ffs_data_closed-on-fs-shutdown.patch
new file mode 100644 (file)
index 0000000..5373312
--- /dev/null
@@ -0,0 +1,84 @@
+From stable+bounces-277542-greg=kroah.com@vger.kernel.org Mon Jul 20 04:36:07 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:47 -0400
+Subject: functionfs: don't abuse ffs_data_closed() on fs shutdown
+To: stable@vger.kernel.org
+Cc: Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720023553.2928009-2-sashal@kernel.org>
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+[ Upstream commit fcb8985143540cbcb8e91c0ea8b7fb5d37c88177 ]
+
+ffs_data_closed() has a seriously confusing logics in it: in addition
+to the normal "decrement a counter and do some work if it hits zero"
+there's "... and if it has somehow become negative, do that" bit.
+
+It's not a race, despite smelling rather fishy.  What really happens
+is that in addition to "call that on close of files there, to match
+the increments of counter on opens" there's one call in ->kill_sb().
+Counter starts at 0 and never goes negative over the lifetime of
+filesystem (or we have much worse problems everywhere - ->release()
+call of some file somehow unpaired with successful ->open() of the
+same).  At the filesystem shutdown it will be 0 or, again, we have
+much worse problems - filesystem instance destroyed with files on it
+still open.  In other words, at that call and at that call alone
+the decrement would go from 0 to -1, hitting that chunk (and not
+hitting the "if it hits 0" part).
+
+So that check is a weirdly spelled "called from ffs_kill_sb()".
+Just expand the call in the latter and kill the misplaced chunk
+in ffs_data_closed().
+
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_fs.c |   16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -2086,12 +2086,18 @@ static int ffs_fs_init_fs_context(struct
+       return 0;
+ }
++static void ffs_data_reset(struct ffs_data *ffs);
++
+ static void
+ ffs_fs_kill_sb(struct super_block *sb)
+ {
+       kill_litter_super(sb);
+-      if (sb->s_fs_info)
+-              ffs_data_closed(sb->s_fs_info);
++      if (sb->s_fs_info) {
++              struct ffs_data *ffs = sb->s_fs_info;
++              ffs->state = FFS_CLOSING;
++              ffs_data_reset(ffs);
++              ffs_data_put(ffs);
++      }
+ }
+ static struct file_system_type ffs_fs_type = {
+@@ -2129,7 +2135,6 @@ static void functionfs_cleanup(void)
+ /* ffs_data and ffs_function construction and destruction code **************/
+ static void ffs_data_clear(struct ffs_data *ffs);
+-static void ffs_data_reset(struct ffs_data *ffs);
+ static void ffs_data_get(struct ffs_data *ffs)
+ {
+@@ -2186,11 +2191,6 @@ static void ffs_data_closed(struct ffs_d
+                       ffs_data_reset(ffs);
+               }
+       }
+-      if (atomic_read(&ffs->opened) < 0) {
+-              ffs->state = FFS_CLOSING;
+-              ffs_data_reset(ffs);
+-      }
+-
+       ffs_data_put(ffs);
+ }
diff --git a/queue-6.18/functionfs-don-t-bother-with-ffs-ref-in-ffs_data_-opened-closed.patch b/queue-6.18/functionfs-don-t-bother-with-ffs-ref-in-ffs_data_-opened-closed.patch
new file mode 100644 (file)
index 0000000..fb5ae1c
--- /dev/null
@@ -0,0 +1,58 @@
+From stable+bounces-277543-greg=kroah.com@vger.kernel.org Mon Jul 20 04:36:01 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:48 -0400
+Subject: functionfs: don't bother with ffs->ref in ffs_data_{opened,closed}()
+To: stable@vger.kernel.org
+Cc: Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720023553.2928009-3-sashal@kernel.org>
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+[ Upstream commit fe47466282a69499ae17a22f064e827badb77078 ]
+
+A reference is held by the superblock (it's dropped in ffs_kill_sb())
+and filesystem will not get to ->kill_sb() while there are any opened
+files, TYVM...
+
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_fs.c |    8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -2143,7 +2143,6 @@ static void ffs_data_get(struct ffs_data
+ static void ffs_data_opened(struct ffs_data *ffs)
+ {
+-      refcount_inc(&ffs->ref);
+       if (atomic_add_return(1, &ffs->opened) == 1 &&
+                       ffs->state == FFS_DEACTIVATED) {
+               ffs->state = FFS_CLOSING;
+@@ -2168,11 +2167,11 @@ static void ffs_data_put(struct ffs_data
+ static void ffs_data_closed(struct ffs_data *ffs)
+ {
+-      struct ffs_epfile *epfiles;
+-      unsigned long flags;
+-
+       if (atomic_dec_and_test(&ffs->opened)) {
+               if (ffs->no_disconnect) {
++                      struct ffs_epfile *epfiles;
++                      unsigned long flags;
++
+                       ffs->state = FFS_DEACTIVATED;
+                       spin_lock_irqsave(&ffs->eps_lock, flags);
+                       epfiles = ffs->epfiles;
+@@ -2191,7 +2190,6 @@ static void ffs_data_closed(struct ffs_d
+                       ffs_data_reset(ffs);
+               }
+       }
+-      ffs_data_put(ffs);
+ }
+ static struct ffs_data *ffs_data_new(const char *dev_name)
diff --git a/queue-6.18/functionfs-switch-to-simple_remove_by_name.patch b/queue-6.18/functionfs-switch-to-simple_remove_by_name.patch
new file mode 100644 (file)
index 0000000..040eee8
--- /dev/null
@@ -0,0 +1,164 @@
+From stable+bounces-277544-greg=kroah.com@vger.kernel.org Mon Jul 20 04:36:02 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:49 -0400
+Subject: functionfs: switch to simple_remove_by_name()
+To: stable@vger.kernel.org
+Cc: Al Viro <viro@zeniv.linux.org.uk>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720023553.2928009-4-sashal@kernel.org>
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+[ Upstream commit c7747fafaba0dcdad3d7da240e961927b4865f98 ]
+
+No need to return dentry from ffs_sb_create_file() or keep it around
+afterwards.
+
+To avoid subtle issues with getting to ffs from epfiles in
+ffs_epfiles_destroy(), pass the superblock as explicit argument.
+Callers have it anyway.
+
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_fs.c |   51 +++++++++++++++----------------------
+ 1 file changed, 22 insertions(+), 29 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -162,8 +162,6 @@ struct ffs_epfile {
+       struct ffs_data                 *ffs;
+       struct ffs_ep                   *ep;    /* P: ffs->eps_lock */
+-      struct dentry                   *dentry;
+-
+       /*
+        * Buffer for holding data from partial reads which may happen since
+        * we’re rounding user read requests to a multiple of a max packet size.
+@@ -273,11 +271,11 @@ struct ffs_desc_helper {
+ };
+ static int  __must_check ffs_epfiles_create(struct ffs_data *ffs);
+-static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
++static void ffs_epfiles_destroy(struct super_block *sb,
++                              struct ffs_epfile *epfiles, unsigned count);
+-static struct dentry *
+-ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
+-                 const struct file_operations *fops);
++static int ffs_sb_create_file(struct super_block *sb, const char *name,
++                            void *data, const struct file_operations *fops);
+ /* Devices management *******************************************************/
+@@ -1881,9 +1879,8 @@ ffs_sb_make_inode(struct super_block *sb
+ }
+ /* Create "regular" file */
+-static struct dentry *ffs_sb_create_file(struct super_block *sb,
+-                                      const char *name, void *data,
+-                                      const struct file_operations *fops)
++static int ffs_sb_create_file(struct super_block *sb, const char *name,
++                            void *data, const struct file_operations *fops)
+ {
+       struct ffs_data *ffs = sb->s_fs_info;
+       struct dentry   *dentry;
+@@ -1891,16 +1888,16 @@ static struct dentry *ffs_sb_create_file
+       dentry = d_alloc_name(sb->s_root, name);
+       if (!dentry)
+-              return NULL;
++              return -ENOMEM;
+       inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
+       if (!inode) {
+               dput(dentry);
+-              return NULL;
++              return -ENOMEM;
+       }
+       d_add(dentry, inode);
+-      return dentry;
++      return 0;
+ }
+ /* Super block */
+@@ -1943,10 +1940,7 @@ static int ffs_sb_fill(struct super_bloc
+               return -ENOMEM;
+       /* EP0 file */
+-      if (!ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations))
+-              return -ENOMEM;
+-
+-      return 0;
++      return ffs_sb_create_file(sb, "ep0", ffs, &ffs_ep0_operations);
+ }
+ enum {
+@@ -2180,7 +2174,7 @@ static void ffs_data_closed(struct ffs_d
+                                                       flags);
+                       if (epfiles)
+-                              ffs_epfiles_destroy(epfiles,
++                              ffs_epfiles_destroy(ffs->sb, epfiles,
+                                                ffs->eps_count);
+                       if (ffs->setup_state == FFS_SETUP_PENDING)
+@@ -2239,7 +2233,7 @@ static void ffs_data_clear(struct ffs_da
+        * copy of epfile will save us from use-after-free.
+        */
+       if (epfiles) {
+-              ffs_epfiles_destroy(epfiles, ffs->eps_count);
++              ffs_epfiles_destroy(ffs->sb, epfiles, ffs->eps_count);
+               ffs->epfiles = NULL;
+       }
+@@ -2336,6 +2330,7 @@ static int ffs_epfiles_create(struct ffs
+ {
+       struct ffs_epfile *epfile, *epfiles;
+       unsigned i, count;
++      int err;
+       count = ffs->eps_count;
+       epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
+@@ -2353,12 +2348,11 @@ static int ffs_epfiles_create(struct ffs
+               else
+                       sprintf(epfile->name, "ep%u", i);
+               epfile->in = (ffs->eps_addrmap[i] & USB_ENDPOINT_DIR_MASK) ? 1 : 0;
+-              epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
+-                                               epfile,
+-                                               &ffs_epfile_operations);
+-              if (!epfile->dentry) {
+-                      ffs_epfiles_destroy(epfiles, i - 1);
+-                      return -ENOMEM;
++              err = ffs_sb_create_file(ffs->sb, epfile->name,
++                                       epfile, &ffs_epfile_operations);
++              if (err) {
++                      ffs_epfiles_destroy(ffs->sb, epfiles, i - 1);
++                      return err;
+               }
+       }
+@@ -2366,16 +2360,15 @@ static int ffs_epfiles_create(struct ffs
+       return 0;
+ }
+-static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
++static void ffs_epfiles_destroy(struct super_block *sb,
++                              struct ffs_epfile *epfiles, unsigned count)
+ {
+       struct ffs_epfile *epfile = epfiles;
++      struct dentry *root = sb->s_root;
+       for (; count; --count, ++epfile) {
+               BUG_ON(mutex_is_locked(&epfile->mutex));
+-              if (epfile->dentry) {
+-                      simple_recursive_removal(epfile->dentry, NULL);
+-                      epfile->dentry = NULL;
+-              }
++              simple_remove_by_name(root, epfile->name, NULL);
+       }
+       kfree(epfiles);
diff --git a/queue-6.18/functionfs-use-spinlock-for-ffs_deactivated-ffs_closing-transitions.patch b/queue-6.18/functionfs-use-spinlock-for-ffs_deactivated-ffs_closing-transitions.patch
new file mode 100644 (file)
index 0000000..0160e35
--- /dev/null
@@ -0,0 +1,114 @@
+From stable+bounces-277545-greg=kroah.com@vger.kernel.org Mon Jul 20 04:36:07 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:50 -0400
+Subject: functionfs: use spinlock for FFS_DEACTIVATED/FFS_CLOSING transitions
+To: stable@vger.kernel.org
+Cc: Al Viro <viro@zeniv.linux.org.uk>, Samuel Wu <wusamuel@google.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720023553.2928009-5-sashal@kernel.org>
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+[ Upstream commit 2005aabe94eaab8608879d98afb901bc99bc3a31 ]
+
+When all files are closed, functionfs needs ffs_data_reset() to be
+done before any further opens are allowed.
+
+During that time we have ffs->state set to FFS_CLOSING; that makes
+->open() fail with -EBUSY.  Once ffs_data_reset() is done, it
+switches state (to FFS_READ_DESCRIPTORS) indicating that opening
+that thing is allowed again.  There's a couple of additional twists:
+       * mounting with -o no_disconnect delays ffs_data_reset()
+from doing that at the final ->release() to the first subsequent
+open().  That's indicated by ffs->state set to FFS_DEACTIVATED;
+if open() sees that, it immediately switches to FFS_CLOSING and
+proceeds with doing ffs_data_reset() before returning to userland.
+       * a couple of usb callbacks need to force the delayed
+transition; unfortunately, they are done in locking environment
+that does not allow blocking and ffs_data_reset() can block.
+As the result, if these callbacks see FFS_DEACTIVATED, they change
+state to FFS_CLOSING and use schedule_work() to get ffs_data_reset()
+executed asynchronously.
+
+Unfortunately, the locking is rather insufficient.  A fix attempted
+in e5bf5ee26663 ("functionfs: fix the open/removal races") had closed
+a bunch of UAF, but it didn't do anything to the callbacks, lacked
+barriers in transition from FFS_CLOSING to FFS_READ_DESCRIPTORS
+_and_ it had been too heavy-handed in open()/open() serialization -
+I've used ffs->mutex for that, and it's being held over actual IO on
+ep0, complete with copy_from_user(), etc.
+
+Even more unfortunately, the userland side is apparently racy enough
+to have the resulting timing changes (no failures, just a delayed
+return of open(2)) disrupt the things quite badly.  Userland bugs
+or not, it's a clear regression that needs to be dealt with.
+
+Solution is to use a spinlock for serializing these state checks and
+transitions - unlike ffs->mutex it can be taken in these callbacks
+and it doesn't disrupt the timings in open().
+
+We could introduce a new spinlock, but it's easier to use the one
+that is already there (ffs->eps_lock) instead - the locking
+environment is safe for it in all affected places.
+
+Since now it is held over all places that alter or check the
+open count (ffs->opened), there's no need to keep that atomic_t -
+int would serve just fine and it's simpler that way.
+
+Fixes: e5bf5ee26663 ("functionfs: fix the open/removal races")
+Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode") # v4.0
+Tested-by: Samuel Wu <wusamuel@google.com>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_fs.c |    8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -3736,6 +3736,7 @@ static int ffs_func_set_alt(struct usb_f
+ {
+       struct ffs_function *func = ffs_func_from_usb(f);
+       struct ffs_data *ffs = func->ffs;
++      unsigned long flags;
+       int ret = 0, intf;
+       if (alt > MAX_ALT_SETTINGS)
+@@ -3748,12 +3749,15 @@ static int ffs_func_set_alt(struct usb_f
+       if (ffs->func)
+               ffs_func_eps_disable(ffs->func);
++      spin_lock_irqsave(&ffs->eps_lock, flags);
+       if (ffs->state == FFS_DEACTIVATED) {
+               ffs->state = FFS_CLOSING;
++              spin_unlock_irqrestore(&ffs->eps_lock, flags);
+               INIT_WORK(&ffs->reset_work, ffs_reset_work);
+               schedule_work(&ffs->reset_work);
+               return -ENODEV;
+       }
++      spin_unlock_irqrestore(&ffs->eps_lock, flags);
+       if (ffs->state != FFS_ACTIVE)
+               return -ENODEV;
+@@ -3771,16 +3775,20 @@ static void ffs_func_disable(struct usb_
+ {
+       struct ffs_function *func = ffs_func_from_usb(f);
+       struct ffs_data *ffs = func->ffs;
++      unsigned long flags;
+       if (ffs->func)
+               ffs_func_eps_disable(ffs->func);
++      spin_lock_irqsave(&ffs->eps_lock, flags);
+       if (ffs->state == FFS_DEACTIVATED) {
+               ffs->state = FFS_CLOSING;
++              spin_unlock_irqrestore(&ffs->eps_lock, flags);
+               INIT_WORK(&ffs->reset_work, ffs_reset_work);
+               schedule_work(&ffs->reset_work);
+               return;
+       }
++      spin_unlock_irqrestore(&ffs->eps_lock, flags);
+       if (ffs->state == FFS_ACTIVE) {
+               ffs->func = NULL;
diff --git a/queue-6.18/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch b/queue-6.18/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch
new file mode 100644 (file)
index 0000000..9421b29
--- /dev/null
@@ -0,0 +1,69 @@
+From stable+bounces-277200-greg=kroah.com@vger.kernel.org Fri Jul 17 21:59:49 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 15:55:16 -0400
+Subject: hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length
+To: stable@vger.kernel.org
+Cc: Tristan Madani <tristan@talencesecurity.com>, syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com, syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com, Viacheslav Dubeyko <slava@dubeyko.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717195516.2194907-2-sashal@kernel.org>
+
+From: Tristan Madani <tristan@talencesecurity.com>
+
+[ Upstream commit 966cb76fb2857a4242cab6ea2ea17acf818a3da7 ]
+
+check_and_correct_requested_length() compares (off + len) against
+node_size using u32 arithmetic.  When the caller passes a large len
+value (e.g. from an underflowed subtraction in hfs_brec_remove()),
+off + len can wrap past 2^32 and produce a small result, causing the
+bounds check to pass when it should fail.
+
+For example, with off=14 and len=0xFFFFFFF2 (underflowed from
+data_off - keyoffset - size in hfs_brec_remove), off + len wraps to 6,
+which is less than a typical node_size of 512, so the check passes and
+the subsequent memmove reads ~4GB past the node buffer.
+
+Fix this by widening the addition to u64 before comparing against
+node_size.  This prevents the u32 wrap while keeping the logic
+straightforward.
+
+Reported-by: syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=6df204b70bf3261691c5
+Tested-by: syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com
+Reported-by: syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=e76bf3d19b85350571ac
+Tested-by: syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com
+Fixes: a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()")
+Cc: stable@vger.kernel.org
+Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
+Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com>
+Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
+Link: https://lore.kernel.org/r/20260505111300.3592757-2-tristmd@gmail.com
+Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/hfs/bnode.c          |    2 +-
+ fs/hfsplus/hfsplus_fs.h |    2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/fs/hfs/bnode.c
++++ b/fs/hfs/bnode.c
+@@ -41,7 +41,7 @@ u32 check_and_correct_requested_length(s
+       node_size = node->tree->node_size;
+-      if ((off + len) > node_size) {
++      if ((u64)off + len > node_size) {
+               u32 new_len = node_size - off;
+               pr_err("requested length has been corrected: "
+--- a/fs/hfsplus/hfsplus_fs.h
++++ b/fs/hfsplus/hfsplus_fs.h
+@@ -585,7 +585,7 @@ u32 check_and_correct_requested_length(s
+       node_size = node->tree->node_size;
+-      if ((off + len) > node_size) {
++      if ((u64)off + len > node_size) {
+               u32 new_len = node_size - off;
+               pr_err("requested length has been corrected: "
diff --git a/queue-6.18/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch b/queue-6.18/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch
new file mode 100644 (file)
index 0000000..5388a86
--- /dev/null
@@ -0,0 +1,957 @@
+From stable+bounces-277201-greg=kroah.com@vger.kernel.org Fri Jul 17 21:59:53 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 15:55:15 -0400
+Subject: hfs/hfsplus: prevent getting negative values of offset/length
+To: stable@vger.kernel.org
+Cc: Viacheslav Dubeyko <slava@dubeyko.com>, John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>, Yangtao Li <frank.li@vivo.com>, linux-fsdevel@vger.kernel.org, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717195516.2194907-1-sashal@kernel.org>
+
+From: Viacheslav Dubeyko <slava@dubeyko.com>
+
+[ Upstream commit 00c14a09a70e10ae18eb3707d0059291425c04bd ]
+
+The syzbot reported KASAN out-of-bounds issue in
+hfs_bnode_move():
+
+[   45.588165][ T9821] hfs: dst 14, src 65536, len -65536
+[   45.588895][ T9821] ==================================================================
+[   45.590114][ T9821] BUG: KASAN: out-of-bounds in hfs_bnode_move+0xfd/0x140
+[   45.591127][ T9821] Read of size 18446744073709486080 at addr ffff888035935400 by task repro/9821
+[   45.592207][ T9821]
+[   45.592420][ T9821] CPU: 0 UID: 0 PID: 9821 Comm: repro Not tainted 6.16.0-rc7-dirty #42 PREEMPT(full)
+[   45.592428][ T9821] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
+[   45.592431][ T9821] Call Trace:
+[   45.592434][ T9821]  <TASK>
+[   45.592437][ T9821]  dump_stack_lvl+0x1c1/0x2a0
+[   45.592446][ T9821]  ? __virt_addr_valid+0x1c8/0x5c0
+[   45.592454][ T9821]  ? __pfx_dump_stack_lvl+0x10/0x10
+[   45.592461][ T9821]  ? rcu_is_watching+0x15/0xb0
+[   45.592469][ T9821]  ? lock_release+0x4b/0x3e0
+[   45.592476][ T9821]  ? __virt_addr_valid+0x1c8/0x5c0
+[   45.592483][ T9821]  ? __virt_addr_valid+0x4a5/0x5c0
+[   45.592491][ T9821]  print_report+0x17e/0x7c0
+[   45.592497][ T9821]  ? __virt_addr_valid+0x1c8/0x5c0
+[   45.592504][ T9821]  ? __virt_addr_valid+0x4a5/0x5c0
+[   45.592511][ T9821]  ? __phys_addr+0xd3/0x180
+[   45.592519][ T9821]  ? hfs_bnode_move+0xfd/0x140
+[   45.592526][ T9821]  kasan_report+0x147/0x180
+[   45.592531][ T9821]  ? _printk+0xcf/0x120
+[   45.592537][ T9821]  ? hfs_bnode_move+0xfd/0x140
+[   45.592544][ T9821]  ? hfs_bnode_move+0xfd/0x140
+[   45.592552][ T9821]  kasan_check_range+0x2b0/0x2c0
+[   45.592557][ T9821]  ? hfs_bnode_move+0xfd/0x140
+[   45.592565][ T9821]  __asan_memmove+0x29/0x70
+[   45.592572][ T9821]  hfs_bnode_move+0xfd/0x140
+[   45.592580][ T9821]  hfs_brec_remove+0x473/0x560
+[   45.592589][ T9821]  hfs_cat_move+0x6fb/0x960
+[   45.592598][ T9821]  ? __pfx_hfs_cat_move+0x10/0x10
+[   45.592607][ T9821]  ? seqcount_lockdep_reader_access+0x122/0x1c0
+[   45.592614][ T9821]  ? lockdep_hardirqs_on+0x9c/0x150
+[   45.592631][ T9821]  ? __lock_acquire+0xaec/0xd80
+[   45.592641][ T9821]  hfs_rename+0x1dc/0x2d0
+[   45.592649][ T9821]  ? __pfx_hfs_rename+0x10/0x10
+[   45.592657][ T9821]  vfs_rename+0xac6/0xed0
+[   45.592664][ T9821]  ? __pfx_vfs_rename+0x10/0x10
+[   45.592670][ T9821]  ? d_alloc+0x144/0x190
+[   45.592677][ T9821]  ? bpf_lsm_path_rename+0x9/0x20
+[   45.592683][ T9821]  ? security_path_rename+0x17d/0x490
+[   45.592691][ T9821]  do_renameat2+0x890/0xc50
+[   45.592699][ T9821]  ? __pfx_do_renameat2+0x10/0x10
+[   45.592707][ T9821]  ? getname_flags+0x1e5/0x540
+[   45.592714][ T9821]  __x64_sys_rename+0x82/0x90
+[   45.592720][ T9821]  ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
+[   45.592725][ T9821]  do_syscall_64+0xf3/0x3a0
+[   45.592741][ T9821]  ? exc_page_fault+0x9f/0xf0
+[   45.592748][ T9821]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
+[   45.592754][ T9821] RIP: 0033:0x7f7f73fe3fc9
+[   45.592760][ T9821] Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 48
+[   45.592765][ T9821] RSP: 002b:00007ffc7e116cf8 EFLAGS: 00000283 ORIG_RAX: 0000000000000052
+[   45.592772][ T9821] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f7f73fe3fc9
+[   45.592776][ T9821] RDX: 0000200000000871 RSI: 0000200000000780 RDI: 00002000000003c0
+[   45.592781][ T9821] RBP: 00007ffc7e116d00 R08: 0000000000000000 R09: 00007ffc7e116d30
+[   45.592784][ T9821] R10: fffffffffffffff0 R11: 0000000000000283 R12: 00005557e81f8250
+[   45.592788][ T9821] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
+[   45.592795][ T9821]  </TASK>
+[   45.592797][ T9821]
+[   45.619721][ T9821] The buggy address belongs to the physical page:
+[   45.620300][ T9821] page: refcount:1 mapcount:1 mapping:0000000000000000 index:0x559a88174 pfn:0x35935
+[   45.621150][ T9821] memcg:ffff88810a1d5b00
+[   45.621531][ T9821] anon flags: 0xfff60000020838(uptodate|dirty|lru|owner_2|swapbacked|node=0|zone=1|lastcpupid=0x7ff)
+[   45.622496][ T9821] raw: 00fff60000020838 ffffea0000d64d88 ffff888021753e10 ffff888029da0771
+[   45.623260][ T9821] raw: 0000000559a88174 0000000000000000 0000000100000000 ffff88810a1d5b00
+[   45.624030][ T9821] page dumped because: kasan: bad access detected
+[   45.624602][ T9821] page_owner tracks the page as allocated
+[   45.625115][ T9821] page last allocated via order 0, migratetype Movable, gfp_mask 0x140dca(GFP_HIGHUSER_MOVABLE|__GFP_ZERO0
+[   45.626685][ T9821]  post_alloc_hook+0x240/0x2a0
+[   45.627127][ T9821]  get_page_from_freelist+0x2101/0x21e0
+[   45.627628][ T9821]  __alloc_frozen_pages_noprof+0x274/0x380
+[   45.628154][ T9821]  alloc_pages_mpol+0x241/0x4b0
+[   45.628593][ T9821]  vma_alloc_folio_noprof+0xe4/0x210
+[   45.629066][ T9821]  folio_prealloc+0x30/0x180
+[   45.629487][ T9821]  __handle_mm_fault+0x34bd/0x5640
+[   45.629957][ T9821]  handle_mm_fault+0x40e/0x8e0
+[   45.630392][ T9821]  do_user_addr_fault+0xa81/0x1390
+[   45.630862][ T9821]  exc_page_fault+0x76/0xf0
+[   45.631273][ T9821]  asm_exc_page_fault+0x26/0x30
+[   45.631712][ T9821] page last free pid 5269 tgid 5269 stack trace:
+[   45.632281][ T9821]  free_unref_folios+0xc73/0x14c0
+[   45.632740][ T9821]  folios_put_refs+0x55b/0x640
+[   45.633177][ T9821]  free_pages_and_swap_cache+0x26d/0x510
+[   45.633685][ T9821]  tlb_flush_mmu+0x3a0/0x680
+[   45.634105][ T9821]  tlb_finish_mmu+0xd4/0x200
+[   45.634525][ T9821]  exit_mmap+0x44c/0xb70
+[   45.634914][ T9821]  __mmput+0x118/0x420
+[   45.635286][ T9821]  exit_mm+0x1da/0x2c0
+[   45.635659][ T9821]  do_exit+0x652/0x2330
+[   45.636039][ T9821]  do_group_exit+0x21c/0x2d0
+[   45.636457][ T9821]  __x64_sys_exit_group+0x3f/0x40
+[   45.636915][ T9821]  x64_sys_call+0x21ba/0x21c0
+[   45.637342][ T9821]  do_syscall_64+0xf3/0x3a0
+[   45.637756][ T9821]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
+[   45.638290][ T9821] page has been migrated, last migrate reason: numa_misplaced
+[   45.638956][ T9821]
+[   45.639173][ T9821] Memory state around the buggy address:
+[   45.639677][ T9821]  ffff888035935300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.640397][ T9821]  ffff888035935380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.641117][ T9821] >ffff888035935400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.641837][ T9821]                    ^
+[   45.642207][ T9821]  ffff888035935480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.642929][ T9821]  ffff888035935500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+[   45.643650][ T9821] ==================================================================
+
+This commit [1] fixes the issue if an offset inside of b-tree node
+or length of the request is bigger than b-tree node. However,
+this fix is still not ready for negative values
+of the offset or length. Moreover, negative values of
+the offset or length doesn't make sense for b-tree's
+operations. Because we could try to access the memory address
+outside of the beginning of memory page's addresses range.
+Also, using of negative values make logic very complicated,
+unpredictable, and we could access the wrong item(s)
+in the b-tree node.
+
+This patch changes b-tree interface by means of converting
+signed integer arguments of offset and length on u32 type.
+Such conversion has goal to prevent of using negative values
+unintentionally or by mistake in b-tree operations.
+
+[1] 'commit a431930c9bac ("hfs: fix slab-out-of-bounds in hfs_bnode_read()")'
+
+Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
+cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
+cc: Yangtao Li <frank.li@vivo.com>
+cc: linux-fsdevel@vger.kernel.org
+Link: https://lore.kernel.org/r/20251002200020.2578311-1-slava@dubeyko.com
+Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
+Stable-dep-of: 966cb76fb285 ("hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/hfs/bfind.c          |    2 -
+ fs/hfs/bnode.c          |   52 ++++++++++++++--------------
+ fs/hfs/brec.c           |    2 -
+ fs/hfs/btree.c          |    2 -
+ fs/hfs/btree.h          |   71 +++++++++++++++++++-------------------
+ fs/hfs/hfs_fs.h         |   88 ++++++++++++++++++++++++++++--------------------
+ fs/hfs/inode.c          |    3 +
+ fs/hfsplus/bfind.c      |    2 -
+ fs/hfsplus/bnode.c      |   60 ++++++++++++++++----------------
+ fs/hfsplus/brec.c       |    2 -
+ fs/hfsplus/btree.c      |    2 -
+ fs/hfsplus/hfsplus_fs.h |   38 ++++++++++----------
+ 12 files changed, 171 insertions(+), 153 deletions(-)
+
+--- a/fs/hfs/bfind.c
++++ b/fs/hfs/bfind.c
+@@ -167,7 +167,7 @@ release:
+       return res;
+ }
+-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
++int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
+ {
+       int res;
+--- a/fs/hfs/bnode.c
++++ b/fs/hfs/bnode.c
+@@ -16,14 +16,14 @@
+ #include "btree.h"
+ static inline
+-bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
++bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
+ {
+       bool is_valid = off < node->tree->node_size;
+       if (!is_valid) {
+               pr_err("requested invalid offset: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d\n",
++                     "node_size %u, offset %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off);
+       }
+@@ -32,7 +32,7 @@ bool is_bnode_offset_valid(struct hfs_bn
+ }
+ static inline
+-int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
++u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
+ {
+       unsigned int node_size;
+@@ -42,12 +42,12 @@ int check_and_correct_requested_length(s
+       node_size = node->tree->node_size;
+       if ((off + len) > node_size) {
+-              int new_len = (int)node_size - off;
++              u32 new_len = node_size - off;
+               pr_err("requested length has been corrected: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, "
+-                     "requested_len %d, corrected_len %d\n",
++                     "node_size %u, offset %u, "
++                     "requested_len %u, corrected_len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len, new_len);
+@@ -57,12 +57,12 @@ int check_and_correct_requested_length(s
+       return len;
+ }
+-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
++void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
+ {
+       struct page *page;
+-      int pagenum;
+-      int bytes_read;
+-      int bytes_to_read;
++      u32 pagenum;
++      u32 bytes_read;
++      u32 bytes_to_read;
+       memset(buf, 0, len);
+@@ -72,7 +72,7 @@ void hfs_bnode_read(struct hfs_bnode *no
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -88,7 +88,7 @@ void hfs_bnode_read(struct hfs_bnode *no
+               if (pagenum >= node->tree->pages_per_bnode)
+                       break;
+               page = node->page[pagenum];
+-              bytes_to_read = min_t(int, len - bytes_read, PAGE_SIZE - off);
++              bytes_to_read = min_t(u32, len - bytes_read, PAGE_SIZE - off);
+               memcpy_from_page(buf + bytes_read, page, off, bytes_to_read);
+@@ -97,7 +97,7 @@ void hfs_bnode_read(struct hfs_bnode *no
+       }
+ }
+-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
++u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
+ {
+       __be16 data;
+       // optimize later...
+@@ -105,7 +105,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode
+       return be16_to_cpu(data);
+ }
+-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
++u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
+ {
+       u8 data;
+       // optimize later...
+@@ -113,10 +113,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *n
+       return data;
+ }
+-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
++void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
+ {
+       struct hfs_btree *tree;
+-      int key_len;
++      u32 key_len;
+       tree = node->tree;
+       if (node->type == HFS_NODE_LEAF ||
+@@ -127,14 +127,14 @@ void hfs_bnode_read_key(struct hfs_bnode
+       if (key_len > sizeof(hfs_btree_key) || key_len < 1) {
+               memset(key, 0, sizeof(hfs_btree_key));
+-              pr_err("hfs: Invalid key length: %d\n", key_len);
++              pr_err("hfs: Invalid key length: %u\n", key_len);
+               return;
+       }
+       hfs_bnode_read(node, key, off, key_len);
+ }
+-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
++void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
+ {
+       struct page *page;
+@@ -144,7 +144,7 @@ void hfs_bnode_write(struct hfs_bnode *n
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -159,20 +159,20 @@ void hfs_bnode_write(struct hfs_bnode *n
+       set_page_dirty(page);
+ }
+-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
++void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
+ {
+       __be16 v = cpu_to_be16(data);
+       // optimize later...
+       hfs_bnode_write(node, &v, off, 2);
+ }
+-void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data)
++void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data)
+ {
+       // optimize later...
+       hfs_bnode_write(node, &data, off, 1);
+ }
+-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
++void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
+ {
+       struct page *page;
+@@ -182,7 +182,7 @@ void hfs_bnode_clear(struct hfs_bnode *n
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -197,8 +197,8 @@ void hfs_bnode_clear(struct hfs_bnode *n
+       set_page_dirty(page);
+ }
+-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
+-              struct hfs_bnode *src_node, int src, int len)
++void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
++                  struct hfs_bnode *src_node, u32 src, u32 len)
+ {
+       struct page *src_page, *dst_page;
+@@ -218,7 +218,7 @@ void hfs_bnode_copy(struct hfs_bnode *ds
+       set_page_dirty(dst_page);
+ }
+-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
++void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
+ {
+       struct page *page;
+       void *ptr;
+--- a/fs/hfs/brec.c
++++ b/fs/hfs/brec.c
+@@ -62,7 +62,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *no
+       return retval;
+ }
+-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
++int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
+ {
+       struct hfs_btree *tree;
+       struct hfs_bnode *node, *new_node;
+--- a/fs/hfs/btree.c
++++ b/fs/hfs/btree.c
+@@ -259,7 +259,7 @@ static struct hfs_bnode *hfs_bmap_new_bm
+ }
+ /* Make sure @tree has enough space for the @rsvd_nodes */
+-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
++int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
+ {
+       struct inode *inode = tree->inode;
+       u32 count;
+--- a/fs/hfs/btree.h
++++ b/fs/hfs/btree.h
+@@ -86,48 +86,49 @@ struct hfs_find_data {
+ /* btree.c */
+-extern struct hfs_btree *hfs_btree_open(struct super_block *, u32, btree_keycmp);
+-extern void hfs_btree_close(struct hfs_btree *);
+-extern void hfs_btree_write(struct hfs_btree *);
+-extern int hfs_bmap_reserve(struct hfs_btree *, int);
+-extern struct hfs_bnode * hfs_bmap_alloc(struct hfs_btree *);
++extern struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id,
++                                      btree_keycmp keycmp);
++extern void hfs_btree_close(struct hfs_btree *tree);
++extern void hfs_btree_write(struct hfs_btree *tree);
++extern int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
++extern struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
+ extern void hfs_bmap_free(struct hfs_bnode *node);
+ /* bnode.c */
+-extern void hfs_bnode_read(struct hfs_bnode *, void *, int, int);
+-extern u16 hfs_bnode_read_u16(struct hfs_bnode *, int);
+-extern u8 hfs_bnode_read_u8(struct hfs_bnode *, int);
+-extern void hfs_bnode_read_key(struct hfs_bnode *, void *, int);
+-extern void hfs_bnode_write(struct hfs_bnode *, void *, int, int);
+-extern void hfs_bnode_write_u16(struct hfs_bnode *, int, u16);
+-extern void hfs_bnode_write_u8(struct hfs_bnode *, int, u8);
+-extern void hfs_bnode_clear(struct hfs_bnode *, int, int);
+-extern void hfs_bnode_copy(struct hfs_bnode *, int,
+-                         struct hfs_bnode *, int, int);
+-extern void hfs_bnode_move(struct hfs_bnode *, int, int, int);
+-extern void hfs_bnode_dump(struct hfs_bnode *);
+-extern void hfs_bnode_unlink(struct hfs_bnode *);
+-extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32);
+-extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32);
+-extern void hfs_bnode_unhash(struct hfs_bnode *);
+-extern void hfs_bnode_free(struct hfs_bnode *);
+-extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32);
+-extern void hfs_bnode_get(struct hfs_bnode *);
+-extern void hfs_bnode_put(struct hfs_bnode *);
++extern void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
++extern u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
++extern u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
++extern void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
++extern void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
++extern void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
++extern void hfs_bnode_write_u8(struct hfs_bnode *node, u32 off, u8 data);
++extern void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
++extern void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
++                         struct hfs_bnode *src_node, u32 src, u32 len);
++extern void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
++extern void hfs_bnode_dump(struct hfs_bnode *node);
++extern void hfs_bnode_unlink(struct hfs_bnode *node);
++extern struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
++extern struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num);
++extern void hfs_bnode_unhash(struct hfs_bnode *node);
++extern void hfs_bnode_free(struct hfs_bnode *node);
++extern struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num);
++extern void hfs_bnode_get(struct hfs_bnode *node);
++extern void hfs_bnode_put(struct hfs_bnode *node);
+ /* brec.c */
+-extern u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *);
+-extern u16 hfs_brec_keylen(struct hfs_bnode *, u16);
+-extern int hfs_brec_insert(struct hfs_find_data *, void *, int);
+-extern int hfs_brec_remove(struct hfs_find_data *);
++extern u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
++extern u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
++extern int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
++extern int hfs_brec_remove(struct hfs_find_data *fd);
+ /* bfind.c */
+-extern int hfs_find_init(struct hfs_btree *, struct hfs_find_data *);
+-extern void hfs_find_exit(struct hfs_find_data *);
+-extern int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *);
+-extern int hfs_brec_find(struct hfs_find_data *);
+-extern int hfs_brec_read(struct hfs_find_data *, void *, int);
+-extern int hfs_brec_goto(struct hfs_find_data *, int);
++extern int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd);
++extern void hfs_find_exit(struct hfs_find_data *fd);
++extern int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd);
++extern int hfs_brec_find(struct hfs_find_data *fd);
++extern int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
++extern int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
+ struct hfs_bnode_desc {
+--- a/fs/hfs/hfs_fs.h
++++ b/fs/hfs/hfs_fs.h
+@@ -140,74 +140,90 @@ struct hfs_sb_info {
+ #define HFS_FLG_ALT_MDB_DIRTY 2
+ /* bitmap.c */
+-extern u32 hfs_vbm_search_free(struct super_block *, u32, u32 *);
+-extern int hfs_clear_vbm_bits(struct super_block *, u16, u16);
++extern u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits);
++extern int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count);
+ /* catalog.c */
+-extern int hfs_cat_keycmp(const btree_key *, const btree_key *);
++extern int hfs_cat_keycmp(const btree_key *key1, const btree_key *key2);
+ struct hfs_find_data;
+-extern int hfs_cat_find_brec(struct super_block *, u32, struct hfs_find_data *);
+-extern int hfs_cat_create(u32, struct inode *, const struct qstr *, struct inode *);
+-extern int hfs_cat_delete(u32, struct inode *, const struct qstr *);
+-extern int hfs_cat_move(u32, struct inode *, const struct qstr *,
+-                      struct inode *, const struct qstr *);
+-extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, const struct qstr *);
++extern int hfs_cat_find_brec(struct super_block *sb, u32 cnid,
++                           struct hfs_find_data *fd);
++extern int hfs_cat_create(u32 cnid, struct inode *dir,
++                        const struct qstr *str, struct inode *inode);
++extern int hfs_cat_delete(u32 cnid, struct inode *dir, const struct qstr *str);
++extern int hfs_cat_move(u32 cnid, struct inode *src_dir,
++                      const struct qstr *src_name,
++                      struct inode *dst_dir,
++                      const struct qstr *dst_name);
++extern void hfs_cat_build_key(struct super_block *sb, btree_key *key,
++                            u32 parent, const struct qstr *name);
+ /* dir.c */
+ extern const struct file_operations hfs_dir_operations;
+ extern const struct inode_operations hfs_dir_inode_operations;
+ /* extent.c */
+-extern int hfs_ext_keycmp(const btree_key *, const btree_key *);
++extern int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2);
+ extern u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off);
+-extern int hfs_free_fork(struct super_block *, struct hfs_cat_file *, int);
+-extern int hfs_ext_write_extent(struct inode *);
+-extern int hfs_extend_file(struct inode *);
+-extern void hfs_file_truncate(struct inode *);
++extern int hfs_free_fork(struct super_block *sb,
++                       struct hfs_cat_file *file, int type);
++extern int hfs_ext_write_extent(struct inode *inode);
++extern int hfs_extend_file(struct inode *inode);
++extern void hfs_file_truncate(struct inode *inode);
+-extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
++extern int hfs_get_block(struct inode *inode, sector_t block,
++                       struct buffer_head *bh_result, int create);
+ /* inode.c */
+ extern const struct address_space_operations hfs_aops;
+ extern const struct address_space_operations hfs_btree_aops;
+ int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
+-              loff_t pos, unsigned len, struct folio **foliop, void **fsdata);
+-extern struct inode *hfs_new_inode(struct inode *, const struct qstr *, umode_t);
+-extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *);
+-extern int hfs_write_inode(struct inode *, struct writeback_control *);
+-extern int hfs_inode_setattr(struct mnt_idmap *, struct dentry *,
+-                           struct iattr *);
++                  loff_t pos, unsigned int len, struct folio **foliop,
++                  void **fsdata);
++extern struct inode *hfs_new_inode(struct inode *dir, const struct qstr *name,
++                                 umode_t mode);
++extern void hfs_inode_write_fork(struct inode *inode, struct hfs_extent *ext,
++                               __be32 *log_size, __be32 *phys_size);
++extern int hfs_write_inode(struct inode *inode, struct writeback_control *wbc);
++extern int hfs_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
++                           struct iattr *attr);
+ extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext,
+-                      __be32 log_size, __be32 phys_size, u32 clump_size);
+-extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *);
+-extern void hfs_evict_inode(struct inode *);
+-extern void hfs_delete_inode(struct inode *);
++                              __be32 __log_size, __be32 phys_size,
++                              u32 clump_size);
++extern struct inode *hfs_iget(struct super_block *sb, struct hfs_cat_key *key,
++                              hfs_cat_rec *rec);
++extern void hfs_evict_inode(struct inode *inode);
++extern void hfs_delete_inode(struct inode *inode);
+ /* attr.c */
+ extern const struct xattr_handler * const hfs_xattr_handlers[];
+ /* mdb.c */
+-extern int hfs_mdb_get(struct super_block *);
+-extern void hfs_mdb_commit(struct super_block *);
+-extern void hfs_mdb_close(struct super_block *);
+-extern void hfs_mdb_put(struct super_block *);
++extern int hfs_mdb_get(struct super_block *sb);
++extern void hfs_mdb_commit(struct super_block *sb);
++extern void hfs_mdb_close(struct super_block *sb);
++extern void hfs_mdb_put(struct super_block *sb);
+ /* part_tbl.c */
+-extern int hfs_part_find(struct super_block *, sector_t *, sector_t *);
++extern int hfs_part_find(struct super_block *sb,
++                       sector_t *part_start, sector_t *part_size);
+ /* string.c */
+ extern const struct dentry_operations hfs_dentry_operations;
+-extern int hfs_hash_dentry(const struct dentry *, struct qstr *);
+-extern int hfs_strcmp(const unsigned char *, unsigned int,
+-                    const unsigned char *, unsigned int);
++extern int hfs_hash_dentry(const struct dentry *dentry, struct qstr *this);
++extern int hfs_strcmp(const unsigned char *s1, unsigned int len1,
++                    const unsigned char *s2, unsigned int len2);
+ extern int hfs_compare_dentry(const struct dentry *dentry,
+-              unsigned int len, const char *str, const struct qstr *name);
++                              unsigned int len, const char *str,
++                              const struct qstr *name);
+ /* trans.c */
+-extern void hfs_asc2mac(struct super_block *, struct hfs_name *, const struct qstr *);
+-extern int hfs_mac2asc(struct super_block *, char *, const struct hfs_name *);
++extern void hfs_asc2mac(struct super_block *sb,
++                      struct hfs_name *out, const struct qstr *in);
++extern int hfs_mac2asc(struct super_block *sb,
++                      char *out, const struct hfs_name *in);
+ /* super.c */
+ extern void hfs_mark_mdb_dirty(struct super_block *sb);
+--- a/fs/hfs/inode.c
++++ b/fs/hfs/inode.c
+@@ -45,7 +45,8 @@ static void hfs_write_failed(struct addr
+ }
+ int hfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
+-              loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
++                  loff_t pos, unsigned int len, struct folio **foliop,
++                  void **fsdata)
+ {
+       int ret;
+--- a/fs/hfsplus/bfind.c
++++ b/fs/hfsplus/bfind.c
+@@ -210,7 +210,7 @@ release:
+       return res;
+ }
+-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
++int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len)
+ {
+       int res;
+--- a/fs/hfsplus/bnode.c
++++ b/fs/hfsplus/bnode.c
+@@ -20,10 +20,10 @@
+ /* Copy a specified range of bytes from the raw data of a node */
+-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
++void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len)
+ {
+       struct page **pagep;
+-      int l;
++      u32 l;
+       memset(buf, 0, len);
+@@ -33,7 +33,7 @@ void hfs_bnode_read(struct hfs_bnode *no
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -45,17 +45,17 @@ void hfs_bnode_read(struct hfs_bnode *no
+       pagep = node->page + (off >> PAGE_SHIFT);
+       off &= ~PAGE_MASK;
+-      l = min_t(int, len, PAGE_SIZE - off);
++      l = min_t(u32, len, PAGE_SIZE - off);
+       memcpy_from_page(buf, *pagep, off, l);
+       while ((len -= l) != 0) {
+               buf += l;
+-              l = min_t(int, len, PAGE_SIZE);
++              l = min_t(u32, len, PAGE_SIZE);
+               memcpy_from_page(buf, *++pagep, 0, l);
+       }
+ }
+-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
++u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off)
+ {
+       __be16 data;
+       /* TODO: optimize later... */
+@@ -63,7 +63,7 @@ u16 hfs_bnode_read_u16(struct hfs_bnode
+       return be16_to_cpu(data);
+ }
+-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
++u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off)
+ {
+       u8 data;
+       /* TODO: optimize later... */
+@@ -71,10 +71,10 @@ u8 hfs_bnode_read_u8(struct hfs_bnode *n
+       return data;
+ }
+-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
++void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off)
+ {
+       struct hfs_btree *tree;
+-      int key_len;
++      u32 key_len;
+       tree = node->tree;
+       if (node->type == HFS_NODE_LEAF ||
+@@ -86,17 +86,17 @@ void hfs_bnode_read_key(struct hfs_bnode
+       if (key_len > sizeof(hfsplus_btree_key) || key_len < 1) {
+               memset(key, 0, sizeof(hfsplus_btree_key));
+-              pr_err("hfsplus: Invalid key length: %d\n", key_len);
++              pr_err("hfsplus: Invalid key length: %u\n", key_len);
+               return;
+       }
+       hfs_bnode_read(node, key, off, key_len);
+ }
+-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
++void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len)
+ {
+       struct page **pagep;
+-      int l;
++      u32 l;
+       if (!is_bnode_offset_valid(node, off))
+               return;
+@@ -104,7 +104,7 @@ void hfs_bnode_write(struct hfs_bnode *n
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -116,29 +116,29 @@ void hfs_bnode_write(struct hfs_bnode *n
+       pagep = node->page + (off >> PAGE_SHIFT);
+       off &= ~PAGE_MASK;
+-      l = min_t(int, len, PAGE_SIZE - off);
++      l = min_t(u32, len, PAGE_SIZE - off);
+       memcpy_to_page(*pagep, off, buf, l);
+       set_page_dirty(*pagep);
+       while ((len -= l) != 0) {
+               buf += l;
+-              l = min_t(int, len, PAGE_SIZE);
++              l = min_t(u32, len, PAGE_SIZE);
+               memcpy_to_page(*++pagep, 0, buf, l);
+               set_page_dirty(*pagep);
+       }
+ }
+-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
++void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data)
+ {
+       __be16 v = cpu_to_be16(data);
+       /* TODO: optimize later... */
+       hfs_bnode_write(node, &v, off, 2);
+ }
+-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
++void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len)
+ {
+       struct page **pagep;
+-      int l;
++      u32 l;
+       if (!is_bnode_offset_valid(node, off))
+               return;
+@@ -146,7 +146,7 @@ void hfs_bnode_clear(struct hfs_bnode *n
+       if (len == 0) {
+               pr_err("requested zero length: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, len %d\n",
++                     "node_size %u, offset %u, len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len);
+               return;
+@@ -158,22 +158,22 @@ void hfs_bnode_clear(struct hfs_bnode *n
+       pagep = node->page + (off >> PAGE_SHIFT);
+       off &= ~PAGE_MASK;
+-      l = min_t(int, len, PAGE_SIZE - off);
++      l = min_t(u32, len, PAGE_SIZE - off);
+       memzero_page(*pagep, off, l);
+       set_page_dirty(*pagep);
+       while ((len -= l) != 0) {
+-              l = min_t(int, len, PAGE_SIZE);
++              l = min_t(u32, len, PAGE_SIZE);
+               memzero_page(*++pagep, 0, l);
+               set_page_dirty(*pagep);
+       }
+ }
+-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
+-                  struct hfs_bnode *src_node, int src, int len)
++void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
++                  struct hfs_bnode *src_node, u32 src, u32 len)
+ {
+       struct page **src_page, **dst_page;
+-      int l;
++      u32 l;
+       hfs_dbg("dst %u, src %u, len %u\n", dst, src, len);
+       if (!len)
+@@ -190,12 +190,12 @@ void hfs_bnode_copy(struct hfs_bnode *ds
+       dst &= ~PAGE_MASK;
+       if (src == dst) {
+-              l = min_t(int, len, PAGE_SIZE - src);
++              l = min_t(u32, len, PAGE_SIZE - src);
+               memcpy_page(*dst_page, src, *src_page, src, l);
+               set_page_dirty(*dst_page);
+               while ((len -= l) != 0) {
+-                      l = min_t(int, len, PAGE_SIZE);
++                      l = min_t(u32, len, PAGE_SIZE);
+                       memcpy_page(*++dst_page, 0, *++src_page, 0, l);
+                       set_page_dirty(*dst_page);
+               }
+@@ -227,11 +227,11 @@ void hfs_bnode_copy(struct hfs_bnode *ds
+       }
+ }
+-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
++void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len)
+ {
+       struct page **src_page, **dst_page;
+       void *src_ptr, *dst_ptr;
+-      int l;
++      u32 l;
+       hfs_dbg("dst %u, src %u, len %u\n", dst, src, len);
+       if (!len)
+@@ -301,7 +301,7 @@ void hfs_bnode_move(struct hfs_bnode *no
+               dst &= ~PAGE_MASK;
+               if (src == dst) {
+-                      l = min_t(int, len, PAGE_SIZE - src);
++                      l = min_t(u32, len, PAGE_SIZE - src);
+                       dst_ptr = kmap_local_page(*dst_page) + src;
+                       src_ptr = kmap_local_page(*src_page) + src;
+@@ -311,7 +311,7 @@ void hfs_bnode_move(struct hfs_bnode *no
+                       kunmap_local(dst_ptr);
+                       while ((len -= l) != 0) {
+-                              l = min_t(int, len, PAGE_SIZE);
++                              l = min_t(u32, len, PAGE_SIZE);
+                               dst_ptr = kmap_local_page(*++dst_page);
+                               src_ptr = kmap_local_page(*++src_page);
+                               memmove(dst_ptr, src_ptr, l);
+--- a/fs/hfsplus/brec.c
++++ b/fs/hfsplus/brec.c
+@@ -60,7 +60,7 @@ u16 hfs_brec_keylen(struct hfs_bnode *no
+       return retval;
+ }
+-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
++int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len)
+ {
+       struct hfs_btree *tree;
+       struct hfs_bnode *node, *new_node;
+--- a/fs/hfsplus/btree.c
++++ b/fs/hfsplus/btree.c
+@@ -344,7 +344,7 @@ static struct hfs_bnode *hfs_bmap_new_bm
+ }
+ /* Make sure @tree has enough space for the @rsvd_nodes */
+-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes)
++int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes)
+ {
+       struct inode *inode = tree->inode;
+       struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
+--- a/fs/hfsplus/hfsplus_fs.h
++++ b/fs/hfsplus/hfsplus_fs.h
+@@ -357,21 +357,21 @@ u32 hfsplus_calc_btree_clump_size(u32 bl
+ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id);
+ void hfs_btree_close(struct hfs_btree *tree);
+ int hfs_btree_write(struct hfs_btree *tree);
+-int hfs_bmap_reserve(struct hfs_btree *tree, int rsvd_nodes);
++int hfs_bmap_reserve(struct hfs_btree *tree, u32 rsvd_nodes);
+ struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree);
+ void hfs_bmap_free(struct hfs_bnode *node);
+ /* bnode.c */
+-void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len);
+-u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off);
+-u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off);
+-void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off);
+-void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len);
+-void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data);
+-void hfs_bnode_clear(struct hfs_bnode *node, int off, int len);
+-void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
+-                  struct hfs_bnode *src_node, int src, int len);
+-void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len);
++void hfs_bnode_read(struct hfs_bnode *node, void *buf, u32 off, u32 len);
++u16 hfs_bnode_read_u16(struct hfs_bnode *node, u32 off);
++u8 hfs_bnode_read_u8(struct hfs_bnode *node, u32 off);
++void hfs_bnode_read_key(struct hfs_bnode *node, void *key, u32 off);
++void hfs_bnode_write(struct hfs_bnode *node, void *buf, u32 off, u32 len);
++void hfs_bnode_write_u16(struct hfs_bnode *node, u32 off, u16 data);
++void hfs_bnode_clear(struct hfs_bnode *node, u32 off, u32 len);
++void hfs_bnode_copy(struct hfs_bnode *dst_node, u32 dst,
++                  struct hfs_bnode *src_node, u32 src, u32 len);
++void hfs_bnode_move(struct hfs_bnode *node, u32 dst, u32 src, u32 len);
+ void hfs_bnode_dump(struct hfs_bnode *node);
+ void hfs_bnode_unlink(struct hfs_bnode *node);
+ struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid);
+@@ -386,7 +386,7 @@ bool hfs_bnode_need_zeroout(struct hfs_b
+ /* brec.c */
+ u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off);
+ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec);
+-int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len);
++int hfs_brec_insert(struct hfs_find_data *fd, void *entry, u32 entry_len);
+ int hfs_brec_remove(struct hfs_find_data *fd);
+ /* bfind.c */
+@@ -399,7 +399,7 @@ int hfs_find_rec_by_key(struct hfs_bnode
+ int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd,
+                   search_strategy_t rec_found);
+ int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare);
+-int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len);
++int hfs_brec_read(struct hfs_find_data *fd, void *rec, u32 rec_len);
+ int hfs_brec_goto(struct hfs_find_data *fd, int cnt);
+ /* catalog.c */
+@@ -560,14 +560,14 @@ hfsplus_btree_lock_class(struct hfs_btre
+ }
+ static inline
+-bool is_bnode_offset_valid(struct hfs_bnode *node, int off)
++bool is_bnode_offset_valid(struct hfs_bnode *node, u32 off)
+ {
+       bool is_valid = off < node->tree->node_size;
+       if (!is_valid) {
+               pr_err("requested invalid offset: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d\n",
++                     "node_size %u, offset %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off);
+       }
+@@ -576,7 +576,7 @@ bool is_bnode_offset_valid(struct hfs_bn
+ }
+ static inline
+-int check_and_correct_requested_length(struct hfs_bnode *node, int off, int len)
++u32 check_and_correct_requested_length(struct hfs_bnode *node, u32 off, u32 len)
+ {
+       unsigned int node_size;
+@@ -586,12 +586,12 @@ int check_and_correct_requested_length(s
+       node_size = node->tree->node_size;
+       if ((off + len) > node_size) {
+-              int new_len = (int)node_size - off;
++              u32 new_len = node_size - off;
+               pr_err("requested length has been corrected: "
+                      "NODE: id %u, type %#x, height %u, "
+-                     "node_size %u, offset %d, "
+-                     "requested_len %d, corrected_len %d\n",
++                     "node_size %u, offset %u, "
++                     "requested_len %u, corrected_len %u\n",
+                      node->this, node->type, node->height,
+                      node->tree->node_size, off, len, new_len);
diff --git a/queue-6.18/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch b/queue-6.18/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch
new file mode 100644 (file)
index 0000000..a99902b
--- /dev/null
@@ -0,0 +1,89 @@
+From stable+bounces-273933-greg=kroah.com@vger.kernel.org Mon Jul 13 19:57:52 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 13 Jul 2026 13:57:33 -0400
+Subject: iio: hid-sensor-rotation: Fix stale or zero output when reading raw values
+To: stable@vger.kernel.org
+Cc: Zhang Lixu <lixu.zhang@intel.com>, Andy Shevchenko <andriy.shevchenko@intel.com>, Stable@vger.kernel.org, Jonathan Cameron <jic23@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260713175733.1907391-2-sashal@kernel.org>
+
+From: Zhang Lixu <lixu.zhang@intel.com>
+
+[ Upstream commit 3ce8d099e0afc5a7da75a2007a67f67c4f5a4af1 ]
+
+When reading the raw quaternion attribute (in_rot_quaternion_raw), the
+driver currently returns either all zeros (if the sensor was never enabled)
+or stale data (if the sensor was previously enabled) because it reads from
+the internal buffer without explicitly requesting a new sample from the
+sensor.
+
+To fix this, power up the sensor, call sensor_hub_input_attr_read_values()
+to issue a synchronous GET_REPORT and receive the full quaternion data
+directly into a local buffer, then decode the four components.
+
+Fixes: fc18dddc0625 ("iio: hid-sensors: Added device rotation support")
+Signed-off-by: Zhang Lixu <lixu.zhang@intel.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <jic23@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/iio/orientation/hid-sensor-rotation.c |   40 ++++++++++++++++++++++++--
+ 1 file changed, 38 insertions(+), 2 deletions(-)
+
+--- a/drivers/iio/orientation/hid-sensor-rotation.c
++++ b/drivers/iio/orientation/hid-sensor-rotation.c
+@@ -74,6 +74,13 @@ static int dev_rot_read_raw(struct iio_d
+                               long mask)
+ {
+       struct dev_rot_state *rot_state = iio_priv(indio_dev);
++      struct hid_sensor_hub_device *hsdev = rot_state->common_attributes.hsdev;
++      struct hid_sensor_hub_attribute_info *info = &rot_state->quaternion;
++      u32 usage_id = HID_USAGE_SENSOR_ORIENT_QUATERNION;
++      union {
++              s16 val16[4];
++              s32 val32[4];
++      } raw_buf;
+       int ret_type;
+       int i;
+@@ -83,8 +90,37 @@ static int dev_rot_read_raw(struct iio_d
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               if (size >= 4) {
+-                      for (i = 0; i < 4; ++i)
+-                              vals[i] = rot_state->scan.sampled_vals[i];
++                      if (info->size <= 0 || info->size > sizeof(raw_buf))
++                              return -EINVAL;
++
++                      hid_sensor_power_state(&rot_state->common_attributes, true);
++
++                      ret_type = sensor_hub_input_attr_read_values(hsdev,
++                                                                   hsdev->usage,
++                                                                   usage_id,
++                                                                   info->report_id,
++                                                                   SENSOR_HUB_SYNC,
++                                                                   info->size,
++                                                                   (u8 *)&raw_buf);
++
++                      hid_sensor_power_state(&rot_state->common_attributes, false);
++
++                      if (ret_type < 0)
++                              return ret_type;
++
++                      switch (info->size) {
++                      case sizeof(raw_buf.val16):
++                              for (i = 0; i < ARRAY_SIZE(raw_buf.val16); i++)
++                                      vals[i] = raw_buf.val16[i];
++                              break;
++                      case sizeof(raw_buf.val32):
++                              for (i = 0; i < ARRAY_SIZE(raw_buf.val32); i++)
++                                      vals[i] = raw_buf.val32[i];
++                              break;
++                      default:
++                              return -EINVAL;
++                      }
++
+                       ret_type = IIO_VAL_INT_MULTIPLE;
+                       *val_len =  4;
+               } else
diff --git a/queue-6.18/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch b/queue-6.18/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch
new file mode 100644 (file)
index 0000000..88e627f
--- /dev/null
@@ -0,0 +1,457 @@
+From stable+bounces-275248-greg=kroah.com@vger.kernel.org Thu Jul 16 13:21:53 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 07:21:20 -0400
+Subject: ksmbd: centralize ksmbd_conn final release to plug transport leak
+To: stable@vger.kernel.org
+Cc: DaeMyung Kang <charsyam@gmail.com>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716112121.1735294-1-sashal@kernel.org>
+
+From: DaeMyung Kang <charsyam@gmail.com>
+
+[ Upstream commit b1f1e80620deb49daf63c2e677046599b693dc1f ]
+
+ksmbd_conn_free() is one of four sites that can observe the last
+refcount drop of a struct ksmbd_conn.  The other three
+
+    fs/smb/server/connection.c    ksmbd_conn_r_count_dec()
+    fs/smb/server/oplock.c        __free_opinfo()
+    fs/smb/server/vfs_cache.c     session_fd_check()
+
+end the conn with a bare kfree(), skipping
+ida_destroy(&conn->async_ida) and
+conn->transport->ops->free_transport(conn->transport).  Whenever one
+of them is the last putter, the embedded async_ida and the entire
+transport struct leak -- for TCP, that is also the struct socket and
+the kvec iov.
+
+__free_opinfo() being a final putter is not theoretical.  opinfo_put()
+queues the callback via call_rcu(&opinfo->rcu, free_opinfo_rcu), so
+ksmbd_server_terminate_conn() can deposit N opinfo releases in RCU and
+have ksmbd_conn_free() run in the handler thread before any of them
+fire.  ksmbd_conn_free() then observes refcnt > 0 and short-circuits;
+the last RCU-delivered __free_opinfo() falls onto its bare kfree(conn)
+branch and the transport is lost.
+
+A/B validation in a QEMU/virtme guest, mounting //127.0.0.1/testshare:
+each iteration holds 8 files open via sleep processes, force-closes
+TCP with "ss -K sport = :445", kills the holders, lazy-umounts;
+repeated 10 times, then ksmbd shutdown and kmemleak scan.
+
+    state         conn_alloc  conn_free  tcp_free  opi_rcu  kmemleak
+    ----------    ----------  ---------  --------  -------  --------
+    pre-patch         20          20        10       160        7
+    with patch        20          20        20       160        0
+
+Pre-patch conn_free=20 with tcp_free=10 directly demonstrates the
+bare-kfree paths skipping transport cleanup; kmemleak backtraces point
+into struct tcp_transport / iov.  With this patch tcp_free matches
+conn_free at 20/20 and kmemleak is clean.
+
+Move the per-struct final release into __ksmbd_conn_release_work() and
+route the three bare-kfree final-put sites through a new
+ksmbd_conn_put().  Those sites now pair ida_destroy() and
+free_transport() with kfree(conn) regardless of which holder happens
+to release the last reference.  stop_sessions() only triggers the
+transport shutdown and does not itself drop the last conn reference,
+so it is unaffected.
+
+The centralized release reaches sock_release() -> tcp_close() ->
+lock_sock_nested() (might_sleep) from every final putter, including
+__free_opinfo() invoked from an RCU softirq callback, which trips
+CONFIG_DEBUG_ATOMIC_SLEEP.  Defer the release to a dedicated
+ksmbd_conn_wq workqueue so ksmbd_conn_put() is safe from any
+non-sleeping context.
+
+Make ksmbd_file own a strong connection reference while fp->conn is
+non-NULL so durable-preserve and final-close paths cannot dereference
+a stale connection.  ksmbd_open_fd() and ksmbd_reopen_durable_fd()
+take the reference via ksmbd_conn_get() (the latter also reorders the
+fp->conn / fp->tcon assignments before __open_id() so the published fp
+is never observed with fp->conn == NULL); session_fd_check() and
+__ksmbd_close_fd() drop it via ksmbd_conn_put().  With that invariant,
+session_fd_check() can take a local conn pointer once and use it
+across the m_op_list and lock_list iterations even though op->conn
+puts may otherwise drop the last reference.
+
+At module exit the workqueue is flushed and destroyed after
+rcu_barrier(), so any release queued by a trailing RCU callback is
+drained before the inode hash and module text go away.
+
+Fixes: ee426bfb9d09 ("ksmbd: add refcnt to ksmbd_conn struct")
+Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/connection.c |  101 ++++++++++++++++++++++++++++++++++++++-------
+ fs/smb/server/connection.h |    6 ++
+ fs/smb/server/oplock.c     |    7 ---
+ fs/smb/server/server.c     |   12 +++++
+ fs/smb/server/vfs_cache.c  |   60 ++++++++++++++++++++++----
+ 5 files changed, 156 insertions(+), 30 deletions(-)
+
+--- a/fs/smb/server/connection.c
++++ b/fs/smb/server/connection.c
+@@ -22,6 +22,81 @@ static struct ksmbd_conn_ops default_con
+ DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS);
+ DECLARE_RWSEM(conn_list_lock);
++static struct workqueue_struct *ksmbd_conn_wq;
++
++int ksmbd_conn_wq_init(void)
++{
++      ksmbd_conn_wq = alloc_workqueue("ksmbd-conn-release",
++                                      WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
++      if (!ksmbd_conn_wq)
++              return -ENOMEM;
++      return 0;
++}
++
++void ksmbd_conn_wq_destroy(void)
++{
++      if (ksmbd_conn_wq) {
++              destroy_workqueue(ksmbd_conn_wq);
++              ksmbd_conn_wq = NULL;
++      }
++}
++
++/*
++ * __ksmbd_conn_release_work() - perform the final, once-per-struct cleanup
++ * of a ksmbd_conn whose refcount has just dropped to zero.
++ *
++ * This is the common release path used by ksmbd_conn_put() for the embedded
++ * state that outlives the connection thread: async_ida and the attached
++ * transport (which owns the socket and iov for TCP).  Called from a workqueue
++ * so that sleep-allowed teardown (sock_release -> tcp_close ->
++ * lock_sock_nested) never runs from an RCU softirq callback (free_opinfo_rcu)
++ * or any other non-sleeping putter context.
++ */
++static void __ksmbd_conn_release_work(struct work_struct *work)
++{
++      struct ksmbd_conn *conn =
++              container_of(work, struct ksmbd_conn, release_work);
++
++      ida_destroy(&conn->async_ida);
++      conn->transport->ops->free_transport(conn->transport);
++      kfree(conn);
++}
++
++/**
++ * ksmbd_conn_get() - take a reference on @conn and return it.
++ *
++ * Returns @conn unchanged so callers can write
++ * "fp->conn = ksmbd_conn_get(work->conn);" in one expression.  Returns NULL
++ * if @conn is NULL.
++ */
++struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn)
++{
++      if (!conn)
++              return NULL;
++
++      atomic_inc(&conn->refcnt);
++      return conn;
++}
++
++/**
++ * ksmbd_conn_put() - drop a reference and, if it was the last, queue the
++ * release onto ksmbd_conn_wq so it runs from process context.
++ *
++ * Callable from any context including RCU softirq callbacks and non-sleeping
++ * locks; the actual release is deferred to the workqueue.  ksmbd_conn_wq is
++ * created in ksmbd_server_init() before any conn can be allocated and is
++ * destroyed in ksmbd_server_exit() after rcu_barrier(), so it is always
++ * non-NULL while a conn reference is held.
++ */
++void ksmbd_conn_put(struct ksmbd_conn *conn)
++{
++      if (!conn)
++              return;
++
++      if (atomic_dec_and_test(&conn->refcnt))
++              queue_work(ksmbd_conn_wq, &conn->release_work);
++}
++
+ /**
+  * ksmbd_conn_free() - free resources of the connection instance
+  *
+@@ -36,23 +111,19 @@ void ksmbd_conn_free(struct ksmbd_conn *
+       hash_del(&conn->hlist);
+       up_write(&conn_list_lock);
++      /*
++       * request_buf / preauth_info / mechToken are only ever accessed by the
++       * connection handler thread that owns @conn.  ksmbd_conn_free() is
++       * called from the transport free_transport() path when that thread is
++       * exiting, so it is safe to release them unconditionally even when
++       * ksmbd_conn_put() below is not the final putter (oplock / ksmbd_file
++       * holders only retain the conn pointer, not these per-thread buffers).
++       */
+       xa_destroy(&conn->sessions);
+       kvfree(conn->request_buf);
+       kfree(conn->preauth_info);
+       kfree(conn->mechToken);
+-      if (atomic_dec_and_test(&conn->refcnt)) {
+-              /*
+-               * async_ida is embedded in struct ksmbd_conn, so pair
+-               * ida_destroy() with the final kfree() rather than with
+-               * the unconditional field teardown above.  This keeps
+-               * the IDA valid for the entire lifetime of the struct,
+-               * even while other refcount holders (oplock / vfs
+-               * durable handles) still reference the connection.
+-               */
+-              ida_destroy(&conn->async_ida);
+-              conn->transport->ops->free_transport(conn->transport);
+-              kfree(conn);
+-      }
++      ksmbd_conn_put(conn);
+ }
+ /**
+@@ -79,6 +150,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void
+               conn->um = ERR_PTR(-EOPNOTSUPP);
+       if (IS_ERR(conn->um))
+               conn->um = NULL;
++      INIT_WORK(&conn->release_work, __ksmbd_conn_release_work);
+       atomic_set(&conn->req_running, 0);
+       atomic_set(&conn->r_count, 0);
+       atomic_set(&conn->refcnt, 1);
+@@ -458,8 +530,7 @@ void ksmbd_conn_r_count_dec(struct ksmbd
+       if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
+               wake_up(&conn->r_count_q);
+-      if (atomic_dec_and_test(&conn->refcnt))
+-              kfree(conn);
++      ksmbd_conn_put(conn);
+ }
+ int ksmbd_conn_transport_init(void)
+--- a/fs/smb/server/connection.h
++++ b/fs/smb/server/connection.h
+@@ -15,6 +15,7 @@
+ #include <linux/kthread.h>
+ #include <linux/nls.h>
+ #include <linux/unicode.h>
++#include <linux/workqueue.h>
+ #include "smb_common.h"
+ #include "ksmbd_work.h"
+@@ -119,6 +120,7 @@ struct ksmbd_conn {
+       bool                            binding;
+       atomic_t                        refcnt;
+       bool                            is_aapl;
++      struct work_struct              release_work;
+ };
+ struct ksmbd_conn_ops {
+@@ -164,6 +166,10 @@ void ksmbd_conn_wait_idle(struct ksmbd_c
+ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id);
+ struct ksmbd_conn *ksmbd_conn_alloc(void);
+ void ksmbd_conn_free(struct ksmbd_conn *conn);
++struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn);
++void ksmbd_conn_put(struct ksmbd_conn *conn);
++int ksmbd_conn_wq_init(void);
++void ksmbd_conn_wq_destroy(void);
+ bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
+ int ksmbd_conn_write(struct ksmbd_work *work);
+ int ksmbd_conn_rdma_read(struct ksmbd_conn *conn,
+--- a/fs/smb/server/oplock.c
++++ b/fs/smb/server/oplock.c
+@@ -30,7 +30,6 @@ static DEFINE_RWLOCK(lease_list_lock);
+ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
+                                       u64 id, __u16 Tid)
+ {
+-      struct ksmbd_conn *conn = work->conn;
+       struct ksmbd_session *sess = work->sess;
+       struct oplock_info *opinfo;
+@@ -39,7 +38,7 @@ static struct oplock_info *alloc_opinfo(
+               return NULL;
+       opinfo->sess = sess;
+-      opinfo->conn = conn;
++      opinfo->conn = ksmbd_conn_get(work->conn);
+       opinfo->level = SMB2_OPLOCK_LEVEL_NONE;
+       opinfo->op_state = OPLOCK_STATE_NONE;
+       opinfo->pending_break = 0;
+@@ -50,7 +49,6 @@ static struct oplock_info *alloc_opinfo(
+       init_waitqueue_head(&opinfo->oplock_brk);
+       atomic_set(&opinfo->refcount, 1);
+       atomic_set(&opinfo->breaking_cnt, 0);
+-      atomic_inc(&opinfo->conn->refcnt);
+       return opinfo;
+ }
+@@ -132,8 +130,7 @@ static void __free_opinfo(struct oplock_
+ {
+       if (opinfo->is_lease)
+               free_lease(opinfo);
+-      if (opinfo->conn && atomic_dec_and_test(&opinfo->conn->refcnt))
+-              kfree(opinfo->conn);
++      ksmbd_conn_put(opinfo->conn);
+       kfree(opinfo);
+ }
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -588,8 +588,14 @@ static int __init ksmbd_server_init(void
+       if (ret)
+               goto err_crypto_destroy;
++      ret = ksmbd_conn_wq_init();
++      if (ret)
++              goto err_workqueue_destroy;
++
+       return 0;
++err_workqueue_destroy:
++      ksmbd_workqueue_destroy();
+ err_crypto_destroy:
+       ksmbd_crypto_destroy();
+ err_release_inode_hash:
+@@ -615,6 +621,12 @@ static void __exit ksmbd_server_exit(voi
+ {
+       ksmbd_server_shutdown();
+       rcu_barrier();
++      /*
++       * ksmbd_conn_put() defers the final release onto ksmbd_conn_wq,
++       * so drain it after rcu_barrier() has fired any pending RCU
++       * callbacks that may have queued a release.
++       */
++      ksmbd_conn_wq_destroy();
+       ksmbd_release_inode_hash();
+ }
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -402,6 +402,17 @@ static void __ksmbd_close_fd(struct ksmb
+               kfree(smb_lock);
+       }
++      /*
++       * Drop fp's strong reference on conn (taken in ksmbd_open_fd() /
++       * ksmbd_reopen_durable_fd()).  Durable fps that reached the
++       * scavenger have already had fp->conn cleared by session_fd_check(),
++       * in which case there is nothing to drop here.
++       */
++      if (fp->conn) {
++              ksmbd_conn_put(fp->conn);
++              fp->conn = NULL;
++      }
++
+       if (ksmbd_stream_fd(fp))
+               kfree(fp->stream.name);
+       kfree(fp->owner.name);
+@@ -694,7 +705,14 @@ struct ksmbd_file *ksmbd_open_fd(struct
+       atomic_set(&fp->refcount, 1);
+       fp->filp                = filp;
+-      fp->conn                = work->conn;
++      /*
++       * fp owns a strong reference on fp->conn for as long as fp->conn is
++       * non-NULL, so session_fd_check() and __ksmbd_close_fd() never
++       * dereference a dangling pointer.  Paired with ksmbd_conn_put() in
++       * session_fd_check() (durable preserve), in __ksmbd_close_fd()
++       * (final close), and on the error paths below.
++       */
++      fp->conn                = ksmbd_conn_get(work->conn);
+       fp->tcon                = work->tcon;
+       fp->volatile_id         = KSMBD_NO_FID;
+       fp->persistent_id       = KSMBD_NO_FID;
+@@ -716,6 +734,8 @@ struct ksmbd_file *ksmbd_open_fd(struct
+       return fp;
+ err_out:
++      /* fp->conn was set and refcounted before every branch here. */
++      ksmbd_conn_put(fp->conn);
+       kmem_cache_free(filp_cache, fp);
+       return ERR_PTR(ret);
+ }
+@@ -1043,25 +1063,32 @@ static bool session_fd_check(struct ksmb
+       if (!is_reconnectable(fp))
+               return false;
++      if (WARN_ON_ONCE(!fp->conn))
++              return false;
++
+       if (ksmbd_vfs_copy_durable_owner(fp, user))
+               return false;
++      /*
++       * fp owns a strong reference on fp->conn (taken in ksmbd_open_fd()
++       * / ksmbd_reopen_durable_fd()), so conn stays valid for the whole
++       * body of this function regardless of any op->conn puts below.
++       */
+       conn = fp->conn;
+       ci = fp->f_ci;
+       down_write(&ci->m_lock);
+       list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
+               if (op->conn != conn)
+                       continue;
+-              if (op->conn && atomic_dec_and_test(&op->conn->refcnt))
+-                      kfree(op->conn);
++              ksmbd_conn_put(op->conn);
+               op->conn = NULL;
+       }
+       up_write(&ci->m_lock);
+       list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
+-              spin_lock(&fp->conn->llist_lock);
++              spin_lock(&conn->llist_lock);
+               list_del_init(&smb_lock->clist);
+-              spin_unlock(&fp->conn->llist_lock);
++              spin_unlock(&conn->llist_lock);
+       }
+       fp->conn = NULL;
+@@ -1072,6 +1099,8 @@ static bool session_fd_check(struct ksmb
+               fp->durable_scavenger_timeout =
+                       jiffies_to_msecs(jiffies) + fp->durable_timeout;
++      /* Drop fp's own reference on conn. */
++      ksmbd_conn_put(conn);
+       return true;
+ }
+@@ -1158,15 +1187,27 @@ int ksmbd_reopen_durable_fd(struct ksmbd
+       old_f_state = fp->f_state;
+       fp->f_state = FP_NEW;
++
++      /*
++       * Initialize fp's connection binding before publishing fp into the
++       * session's file table.  If __open_id() is ordered first, a
++       * concurrent teardown that iterates the table can observe a valid
++       * volatile_id with fp->conn == NULL and preserve a
++       * partially-initialized fp.  fp owns a strong reference on the new
++       * conn (see ksmbd_open_fd()); undo it on __open_id() failure.
++       */
++      fp->conn = ksmbd_conn_get(conn);
++      fp->tcon = work->tcon;
++
+       __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
+       if (!has_file_id(fp->volatile_id)) {
++              fp->conn = NULL;
++              fp->tcon = NULL;
++              ksmbd_conn_put(conn);
+               fp->f_state = old_f_state;
+               return -EBADF;
+       }
+-      fp->conn = conn;
+-      fp->tcon = work->tcon;
+-
+       list_for_each_entry(smb_lock, &fp->lock_list, flist) {
+               spin_lock(&conn->llist_lock);
+               list_add_tail(&smb_lock->clist, &conn->lock_list);
+@@ -1178,8 +1219,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd
+       list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
+               if (op->conn)
+                       continue;
+-              op->conn = fp->conn;
+-              atomic_inc(&op->conn->refcnt);
++              op->conn = ksmbd_conn_get(fp->conn);
+       }
+       up_write(&ci->m_lock);
diff --git a/queue-6.18/ksmbd-fix-path-resolution-in-ksmbd_vfs_kern_path_create.patch b/queue-6.18/ksmbd-fix-path-resolution-in-ksmbd_vfs_kern_path_create.patch
new file mode 100644 (file)
index 0000000..b6b8de1
--- /dev/null
@@ -0,0 +1,145 @@
+From stable+bounces-275234-greg=kroah.com@vger.kernel.org Thu Jul 16 12:19:38 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 06:18:49 -0400
+Subject: ksmbd: fix path resolution in ksmbd_vfs_kern_path_create
+To: stable@vger.kernel.org
+Cc: Davide Ornaghi <d.ornaghi97@gmail.com>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716101849.1464192-1-sashal@kernel.org>
+
+From: Davide Ornaghi <d.ornaghi97@gmail.com>
+
+[ Upstream commit 1c8951963d8ed357f70f59e0ad4ddce2199d2016 ]
+
+The SMB2 open lookup is rooted at the share with LOOKUP_BENEATH, but the
+create/mkdir/hardlink sink is not: ksmbd_vfs_kern_path_create() builds an
+absolute path with convert_to_unix_name() and resolves it from AT_FDCWD
+via start_creating_path(), so a ".." component is walked from the real
+filesystem root and escapes the export.
+
+An authenticated client races a missing path component so the rooted open
+lookup returns -ENOENT (taking the create branch) while the same component
+is present (a directory) when the create walk runs; the create then
+resolves ".." out of the share.
+
+Root the create walk at the share like the lookup and rename paths already
+are: resolve the parent with vfs_path_parent_lookup(..., LOOKUP_BENEATH,
+&share_conf->vfs_path) and create the final component with
+start_creating_noperm(). convert_to_unix_name() then has no callers and is
+removed.
+
+Fixes: 265fd1991c1d ("ksmbd: use LOOKUP_BENEATH to prevent the out of share access")
+Cc: stable@vger.kernel.org
+Signed-off-by: Davide Ornaghi <d.ornaghi97@gmail.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+[ replaced start_creating_noperm() and CLASS(filename_kernel) with open-coded getname_kernel()/inode_lock_nested()/lookup_one_qstr_excl() equivalents absent in 6.18 ]
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/misc.c |   33 ---------------------------------
+ fs/smb/server/misc.h |    1 -
+ fs/smb/server/vfs.c  |   38 ++++++++++++++++++++++++++++++++------
+ 3 files changed, 32 insertions(+), 40 deletions(-)
+
+--- a/fs/smb/server/misc.c
++++ b/fs/smb/server/misc.c
+@@ -278,39 +278,6 @@ char *ksmbd_extract_sharename(struct uni
+       return ksmbd_casefold_sharename(um, name);
+ }
+-/**
+- * convert_to_unix_name() - convert windows name to unix format
+- * @share:    ksmbd_share_config pointer
+- * @name:     file name that is relative to share
+- *
+- * Return:    converted name on success, otherwise NULL
+- */
+-char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name)
+-{
+-      int no_slash = 0, name_len, path_len;
+-      char *new_name;
+-
+-      if (name[0] == '/')
+-              name++;
+-
+-      path_len = share->path_sz;
+-      name_len = strlen(name);
+-      new_name = kmalloc(path_len + name_len + 2, KSMBD_DEFAULT_GFP);
+-      if (!new_name)
+-              return new_name;
+-
+-      memcpy(new_name, share->path, path_len);
+-      if (new_name[path_len - 1] != '/') {
+-              new_name[path_len] = '/';
+-              no_slash = 1;
+-      }
+-
+-      memcpy(new_name + path_len + no_slash, name, name_len);
+-      path_len += name_len + no_slash;
+-      new_name[path_len] = 0x00;
+-      return new_name;
+-}
+-
+ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
+                                 const struct nls_table *local_nls,
+                                 int *conv_len)
+--- a/fs/smb/server/misc.h
++++ b/fs/smb/server/misc.h
+@@ -22,7 +22,6 @@ void ksmbd_strip_last_slash(char *path);
+ void ksmbd_conv_path_to_windows(char *path);
+ char *ksmbd_casefold_sharename(struct unicode_map *um, const char *name);
+ char *ksmbd_extract_sharename(struct unicode_map *um, const char *treename);
+-char *convert_to_unix_name(struct ksmbd_share_config *share, const char *name);
+ #define KSMBD_DIR_INFO_ALIGNMENT      8
+ struct ksmbd_dir_info;
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -1338,15 +1338,41 @@ struct dentry *ksmbd_vfs_kern_path_creat
+                                         unsigned int flags,
+                                         struct path *path)
+ {
+-      char *abs_name;
++      struct ksmbd_share_config *share_conf = work->tcon->share_conf;
++      struct filename *filename __free(putname) = NULL;
++      struct qstr last;
+       struct dentry *dent;
++      int err, type;
+-      abs_name = convert_to_unix_name(work->tcon->share_conf, name);
+-      if (!abs_name)
+-              return ERR_PTR(-ENOMEM);
++      /* resolve the name beneath the share root so ".." cannot escape */
++      filename = getname_kernel(name);
++      if (IS_ERR(filename))
++              return ERR_CAST(filename);
+-      dent = start_creating_path(AT_FDCWD, abs_name, path, flags);
+-      kfree(abs_name);
++      err = vfs_path_parent_lookup(filename, flags | LOOKUP_BENEATH,
++                                   path, &last, &type,
++                                   &share_conf->vfs_path);
++      if (err)
++              return ERR_PTR(err);
++
++      if (unlikely(type != LAST_NORM)) {
++              path_put(path);
++              return ERR_PTR(-EINVAL);
++      }
++
++      err = mnt_want_write(path->mnt);
++      if (err) {
++              path_put(path);
++              return ERR_PTR(err);
++      }
++
++      inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
++      dent = lookup_one_qstr_excl(&last, path->dentry, LOOKUP_CREATE);
++      if (IS_ERR(dent)) {
++              inode_unlock(path->dentry->d_inode);
++              mnt_drop_write(path->mnt);
++              path_put(path);
++      }
+       return dent;
+ }
diff --git a/queue-6.18/ksmbd-track-the-connection-owning-a-byte-range-lock.patch b/queue-6.18/ksmbd-track-the-connection-owning-a-byte-range-lock.patch
new file mode 100644 (file)
index 0000000..69621bf
--- /dev/null
@@ -0,0 +1,138 @@
+From stable+bounces-275249-greg=kroah.com@vger.kernel.org Thu Jul 16 13:21:32 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 07:21:21 -0400
+Subject: ksmbd: track the connection owning a byte-range lock
+To: stable@vger.kernel.org
+Cc: Namjae Jeon <linkinjeon@kernel.org>, Musaab Khan <musaab.khan@protonmail.com>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716112121.1735294-2-sashal@kernel.org>
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c1016dd1d8b2bcd1158bbaabe94a31bb7e7431fb ]
+
+SMB2_LOCK adds each granted byte-range lock to both the file lock list
+and the lock list of the connection which handled the request.  The
+final close and durable handle paths, however, remove the connection
+list entry while holding fp->conn->llist_lock.
+
+With SMB3 multichannel, the connection handling the LOCK request can be
+different from the connection which opened the file.  The entry can
+therefore be removed under a different spinlock from the one protecting
+the list it belongs to.  A concurrent traversal can then access freed
+struct ksmbd_lock and struct file_lock objects.
+
+Record the connection owning each lock's clist entry and hold a
+reference to it while the entry is linked.  Use that connection and its
+llist_lock for unlock, rollback, close, and durable preserve.  Durable
+reconnect assigns the new connection as the owner when publishing the
+locks again.
+
+Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel")
+Cc: stable@vger.kernel.org
+Reported-by: Musaab Khan <musaab.khan@protonmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c   |   10 ++++++++--
+ fs/smb/server/vfs_cache.c |   23 +++++++++++++++++------
+ fs/smb/server/vfs_cache.h |    1 +
+ 3 files changed, 26 insertions(+), 8 deletions(-)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -7660,9 +7660,11 @@ int smb2_lock(struct ksmbd_work *work)
+                                               nolock = 0;
+                                               list_del(&cmp_lock->flist);
+                                               list_del(&cmp_lock->clist);
++                                              cmp_lock->conn = NULL;
+                                               spin_unlock(&conn->llist_lock);
+                                               up_read(&conn_list_lock);
++                                              ksmbd_conn_put(conn);
+                                               locks_free_lock(cmp_lock->fl);
+                                               kfree(cmp_lock);
+                                               goto out_check_cl;
+@@ -7797,6 +7799,7 @@ skip:
+                               goto out2;
+                       } else if (!rc) {
+                               list_add(&smb_lock->llist, &rollback_list);
++                              smb_lock->conn = ksmbd_conn_get(work->conn);
+                               spin_lock(&work->conn->llist_lock);
+                               list_add_tail(&smb_lock->clist,
+                                             &work->conn->lock_list);
+@@ -7851,11 +7854,14 @@ out:
+               }
+               list_del(&smb_lock->llist);
+-              spin_lock(&work->conn->llist_lock);
++              conn = smb_lock->conn;
++              spin_lock(&conn->llist_lock);
+               if (!list_empty(&smb_lock->flist))
+                       list_del(&smb_lock->flist);
+               list_del(&smb_lock->clist);
+-              spin_unlock(&work->conn->llist_lock);
++              smb_lock->conn = NULL;
++              spin_unlock(&conn->llist_lock);
++              ksmbd_conn_put(conn);
+               locks_free_lock(smb_lock->fl);
+               if (rlock)
+--- a/fs/smb/server/vfs_cache.c
++++ b/fs/smb/server/vfs_cache.c
+@@ -391,10 +391,14 @@ static void __ksmbd_close_fd(struct ksmb
+        * there are not accesses to fp->lock_list.
+        */
+       list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
+-              if (!list_empty(&smb_lock->clist) && fp->conn) {
+-                      spin_lock(&fp->conn->llist_lock);
+-                      list_del(&smb_lock->clist);
+-                      spin_unlock(&fp->conn->llist_lock);
++              struct ksmbd_conn *conn = smb_lock->conn;
++
++              if (conn) {
++                      spin_lock(&conn->llist_lock);
++                      list_del_init(&smb_lock->clist);
++                      smb_lock->conn = NULL;
++                      spin_unlock(&conn->llist_lock);
++                      ksmbd_conn_put(conn);
+               }
+               list_del(&smb_lock->flist);
+@@ -1086,9 +1090,15 @@ static bool session_fd_check(struct ksmb
+       up_write(&ci->m_lock);
+       list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
+-              spin_lock(&conn->llist_lock);
++              struct ksmbd_conn *lock_conn = smb_lock->conn;
++
++              if (!lock_conn)
++                      continue;
++              spin_lock(&lock_conn->llist_lock);
+               list_del_init(&smb_lock->clist);
+-              spin_unlock(&conn->llist_lock);
++              smb_lock->conn = NULL;
++              spin_unlock(&lock_conn->llist_lock);
++              ksmbd_conn_put(lock_conn);
+       }
+       fp->conn = NULL;
+@@ -1209,6 +1219,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd
+       }
+       list_for_each_entry(smb_lock, &fp->lock_list, flist) {
++              smb_lock->conn = ksmbd_conn_get(conn);
+               spin_lock(&conn->llist_lock);
+               list_add_tail(&smb_lock->clist, &conn->lock_list);
+               spin_unlock(&conn->llist_lock);
+--- a/fs/smb/server/vfs_cache.h
++++ b/fs/smb/server/vfs_cache.h
+@@ -32,6 +32,7 @@ struct ksmbd_session;
+ struct ksmbd_lock {
+       struct file_lock *fl;
++      struct ksmbd_conn *conn;
+       struct list_head clist;
+       struct list_head flist;
+       struct list_head llist;
diff --git a/queue-6.18/ksmbd-use-hmac-md5-library-for-ntlmv2.patch b/queue-6.18/ksmbd-use-hmac-md5-library-for-ntlmv2.patch
new file mode 100644 (file)
index 0000000..3e97d3c
--- /dev/null
@@ -0,0 +1,350 @@
+From stable+bounces-275253-greg=kroah.com@vger.kernel.org Thu Jul 16 13:22:08 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 07:21:46 -0400
+Subject: ksmbd: Use HMAC-MD5 library for NTLMv2
+To: stable@vger.kernel.org
+Cc: Eric Biggers <ebiggers@kernel.org>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716112147.1735816-3-sashal@kernel.org>
+
+From: Eric Biggers <ebiggers@kernel.org>
+
+[ Upstream commit 3a597e6e9701eb7af04864ffdc0a6a91bc8c6496 ]
+
+For the HMAC-MD5 computations in NTLMv2, use the HMAC-MD5 library
+instead of a "hmac(md5)" crypto_shash.  This is simpler and faster.
+With the library there's no need to allocate memory, no need to handle
+errors, and the HMAC-MD5 code is accessed directly without inefficient
+indirect calls and other unnecessary API overhead.
+
+To preserve the existing behavior of NTLMv2 support being disabled when
+the kernel is booted with "fips=1", make ksmbd_auth_ntlmv2() check
+fips_enabled itself.  Previously it relied on the error from
+crypto_alloc_shash("hmac(md5)") being bubbled up.  I don't know for sure
+that this is actually needed, but this preserves the existing behavior.
+
+Signed-off-by: Eric Biggers <ebiggers@kernel.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 954d196bebb2 ("ksmbd: validate NTLMv2 response before updating session key")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/Kconfig      |    3 
+ fs/smb/server/auth.c       |  175 ++++++---------------------------------------
+ fs/smb/server/crypto_ctx.c |    8 --
+ fs/smb/server/crypto_ctx.h |    6 -
+ fs/smb/server/server.c     |    2 
+ 5 files changed, 28 insertions(+), 166 deletions(-)
+
+--- a/fs/smb/server/Kconfig
++++ b/fs/smb/server/Kconfig
+@@ -7,11 +7,10 @@ config SMB_SERVER
+       select NLS_UTF8
+       select NLS_UCS2_UTILS
+       select CRYPTO
+-      select CRYPTO_MD5
+-      select CRYPTO_HMAC
+       select CRYPTO_ECB
+       select CRYPTO_LIB_ARC4
+       select CRYPTO_LIB_DES
++      select CRYPTO_LIB_MD5
+       select CRYPTO_LIB_SHA256
+       select CRYPTO_LIB_SHA512
+       select CRYPTO_LIB_UTILS
+--- a/fs/smb/server/auth.c
++++ b/fs/smb/server/auth.c
+@@ -13,6 +13,7 @@
+ #include <linux/xattr.h>
+ #include <crypto/hash.h>
+ #include <crypto/aead.h>
++#include <crypto/md5.h>
+ #include <crypto/sha2.h>
+ #include <crypto/utils.h>
+ #include <linux/random.h>
+@@ -71,85 +72,16 @@ void ksmbd_copy_gss_neg_header(void *buf
+       memcpy(buf, NEGOTIATE_GSS_HEADER, AUTH_GSS_LENGTH);
+ }
+-/**
+- * ksmbd_gen_sess_key() - function to generate session key
+- * @sess:     session of connection
+- * @hash:     source hash value to be used for find session key
+- * @hmac:     source hmac value to be used for finding session key
+- *
+- */
+-static int ksmbd_gen_sess_key(struct ksmbd_session *sess, char *hash,
+-                            char *hmac)
+-{
+-      struct ksmbd_crypto_ctx *ctx;
+-      int rc;
+-
+-      ctx = ksmbd_crypto_ctx_find_hmacmd5();
+-      if (!ctx) {
+-              ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
+-              return -ENOMEM;
+-      }
+-
+-      rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
+-                               hash,
+-                               CIFS_HMAC_MD5_HASH_SIZE);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "hmacmd5 set key fail error %d\n", rc);
+-              goto out;
+-      }
+-
+-      rc = crypto_shash_init(CRYPTO_HMACMD5(ctx));
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not init hmacmd5 error %d\n", rc);
+-              goto out;
+-      }
+-
+-      rc = crypto_shash_update(CRYPTO_HMACMD5(ctx),
+-                               hmac,
+-                               SMB2_NTLMV2_SESSKEY_SIZE);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "Could not update with response error %d\n", rc);
+-              goto out;
+-      }
+-
+-      rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), sess->sess_key);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n", rc);
+-              goto out;
+-      }
+-
+-out:
+-      ksmbd_release_crypto_ctx(ctx);
+-      return rc;
+-}
+-
+ static int calc_ntlmv2_hash(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+                           char *ntlmv2_hash, char *dname)
+ {
+       int ret, len, conv_len;
+       wchar_t *domain = NULL;
+       __le16 *uniname = NULL;
+-      struct ksmbd_crypto_ctx *ctx;
++      struct hmac_md5_ctx ctx;
+-      ctx = ksmbd_crypto_ctx_find_hmacmd5();
+-      if (!ctx) {
+-              ksmbd_debug(AUTH, "can't generate ntlmv2 hash\n");
+-              return -ENOMEM;
+-      }
+-
+-      ret = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
+-                                user_passkey(sess->user),
++      hmac_md5_init_usingrawkey(&ctx, user_passkey(sess->user),
+                                 CIFS_ENCPWD_SIZE);
+-      if (ret) {
+-              ksmbd_debug(AUTH, "Could not set NT Hash as a key\n");
+-              goto out;
+-      }
+-
+-      ret = crypto_shash_init(CRYPTO_HMACMD5(ctx));
+-      if (ret) {
+-              ksmbd_debug(AUTH, "could not init hmacmd5\n");
+-              goto out;
+-      }
+       /* convert user_name to unicode */
+       len = strlen(user_name(sess->user));
+@@ -167,13 +99,7 @@ static int calc_ntlmv2_hash(struct ksmbd
+       }
+       UniStrupr(uniname);
+-      ret = crypto_shash_update(CRYPTO_HMACMD5(ctx),
+-                                (char *)uniname,
+-                                UNICODE_LEN(conv_len));
+-      if (ret) {
+-              ksmbd_debug(AUTH, "Could not update with user\n");
+-              goto out;
+-      }
++      hmac_md5_update(&ctx, (const u8 *)uniname, UNICODE_LEN(conv_len));
+       /* Convert domain name or conn name to unicode and uppercase */
+       len = strlen(dname);
+@@ -190,21 +116,12 @@ static int calc_ntlmv2_hash(struct ksmbd
+               goto out;
+       }
+-      ret = crypto_shash_update(CRYPTO_HMACMD5(ctx),
+-                                (char *)domain,
+-                                UNICODE_LEN(conv_len));
+-      if (ret) {
+-              ksmbd_debug(AUTH, "Could not update with domain\n");
+-              goto out;
+-      }
+-
+-      ret = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_hash);
+-      if (ret)
+-              ksmbd_debug(AUTH, "Could not generate md5 hash\n");
++      hmac_md5_update(&ctx, (const u8 *)domain, UNICODE_LEN(conv_len));
++      hmac_md5_final(&ctx, ntlmv2_hash);
++      ret = 0;
+ out:
+       kfree(uniname);
+       kfree(domain);
+-      ksmbd_release_crypto_ctx(ctx);
+       return ret;
+ }
+@@ -225,74 +142,34 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn
+ {
+       char ntlmv2_hash[CIFS_ENCPWD_SIZE];
+       char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
+-      struct ksmbd_crypto_ctx *ctx = NULL;
+-      char *construct = NULL;
+-      int rc, len;
+-
+-      rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc);
+-              goto out;
+-      }
+-
+-      ctx = ksmbd_crypto_ctx_find_hmacmd5();
+-      if (!ctx) {
+-              ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
+-              return -ENOMEM;
+-      }
+-
+-      rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
+-                               ntlmv2_hash,
+-                               CIFS_HMAC_MD5_HASH_SIZE);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "Could not set NTLMV2 Hash as a key\n");
+-              goto out;
+-      }
+-
+-      rc = crypto_shash_init(CRYPTO_HMACMD5(ctx));
+-      if (rc) {
+-              ksmbd_debug(AUTH, "Could not init hmacmd5\n");
+-              goto out;
+-      }
++      struct hmac_md5_ctx ctx;
++      int rc;
+-      len = CIFS_CRYPTO_KEY_SIZE + blen;
+-      construct = kzalloc(len, KSMBD_DEFAULT_GFP);
+-      if (!construct) {
+-              rc = -ENOMEM;
+-              goto out;
++      if (fips_enabled) {
++              ksmbd_debug(AUTH, "NTLMv2 support is disabled due to FIPS\n");
++              return -EOPNOTSUPP;
+       }
+-      memcpy(construct, cryptkey, CIFS_CRYPTO_KEY_SIZE);
+-      memcpy(construct + CIFS_CRYPTO_KEY_SIZE, &ntlmv2->blob_signature, blen);
+-
+-      rc = crypto_shash_update(CRYPTO_HMACMD5(ctx), construct, len);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "Could not update with response\n");
+-              goto out;
+-      }
+-
+-      rc = crypto_shash_final(CRYPTO_HMACMD5(ctx), ntlmv2_rsp);
++      rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name);
+       if (rc) {
+-              ksmbd_debug(AUTH, "Could not generate md5 hash\n");
+-              goto out;
++              ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc);
++              return rc;
+       }
+-      ksmbd_release_crypto_ctx(ctx);
+-      ctx = NULL;
+-      rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "Could not generate sess key\n");
+-              goto out;
+-      }
++      hmac_md5_init_usingrawkey(&ctx, ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
++      hmac_md5_update(&ctx, cryptkey, CIFS_CRYPTO_KEY_SIZE);
++      hmac_md5_update(&ctx, (const u8 *)&ntlmv2->blob_signature, blen);
++      hmac_md5_final(&ctx, ntlmv2_rsp);
++
++      /* Generate the session key */
++      hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
++                           ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE,
++                           sess->sess_key);
+       if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp,
+                         CIFS_HMAC_MD5_HASH_SIZE))
+-              rc = -EINVAL;
+-out:
+-      if (ctx)
+-              ksmbd_release_crypto_ctx(ctx);
+-      kfree(construct);
+-      return rc;
++              return -EINVAL;
++      return 0;
+ }
+ /**
+--- a/fs/smb/server/crypto_ctx.c
++++ b/fs/smb/server/crypto_ctx.c
+@@ -66,9 +66,6 @@ static struct shash_desc *alloc_shash_de
+       struct shash_desc *shash;
+       switch (id) {
+-      case CRYPTO_SHASH_HMACMD5:
+-              tfm = crypto_alloc_shash("hmac(md5)", 0, 0);
+-              break;
+       case CRYPTO_SHASH_CMACAES:
+               tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
+               break;
+@@ -174,11 +171,6 @@ static struct ksmbd_crypto_ctx *____cryp
+       return NULL;
+ }
+-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void)
+-{
+-      return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5);
+-}
+-
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
+ {
+       return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
+--- a/fs/smb/server/crypto_ctx.h
++++ b/fs/smb/server/crypto_ctx.h
+@@ -10,8 +10,7 @@
+ #include <crypto/aead.h>
+ enum {
+-      CRYPTO_SHASH_HMACMD5    = 0,
+-      CRYPTO_SHASH_CMACAES,
++      CRYPTO_SHASH_CMACAES    = 0,
+       CRYPTO_SHASH_MAX,
+ };
+@@ -33,17 +32,14 @@ struct ksmbd_crypto_ctx {
+       struct crypto_aead              *ccmaes[CRYPTO_AEAD_MAX];
+ };
+-#define CRYPTO_HMACMD5(c)     ((c)->desc[CRYPTO_SHASH_HMACMD5])
+ #define CRYPTO_CMACAES(c)     ((c)->desc[CRYPTO_SHASH_CMACAES])
+-#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm)
+ #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
+ #define CRYPTO_GCM(c)         ((c)->ccmaes[CRYPTO_AEAD_AES_GCM])
+ #define CRYPTO_CCM(c)         ((c)->ccmaes[CRYPTO_AEAD_AES_CCM])
+ void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
+-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -634,8 +634,6 @@ MODULE_AUTHOR("Namjae Jeon <linkinjeon@k
+ MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
+ MODULE_LICENSE("GPL");
+ MODULE_SOFTDEP("pre: ecb");
+-MODULE_SOFTDEP("pre: hmac");
+-MODULE_SOFTDEP("pre: md5");
+ MODULE_SOFTDEP("pre: nls");
+ MODULE_SOFTDEP("pre: aes");
+ MODULE_SOFTDEP("pre: cmac");
diff --git a/queue-6.18/ksmbd-use-hmac-sha256-library-for-message-signing-and-key-generation.patch b/queue-6.18/ksmbd-use-hmac-sha256-library-for-message-signing-and-key-generation.patch
new file mode 100644 (file)
index 0000000..df3f90c
--- /dev/null
@@ -0,0 +1,455 @@
+From stable+bounces-275252-greg=kroah.com@vger.kernel.org Thu Jul 16 13:22:24 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 07:21:45 -0400
+Subject: ksmbd: Use HMAC-SHA256 library for message signing and key generation
+To: stable@vger.kernel.org
+Cc: Eric Biggers <ebiggers@kernel.org>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716112147.1735816-2-sashal@kernel.org>
+
+From: Eric Biggers <ebiggers@kernel.org>
+
+[ Upstream commit 924067ef183bd17f39d790752190f99982524598 ]
+
+Convert ksmbd_sign_smb2_pdu() and generate_key() to use the HMAC-SHA256
+library instead of a "hmac(sha256)" crypto_shash.  This is simpler and
+faster.  With the library there's no need to allocate memory, no need to
+handle errors, and the HMAC-SHA256 code is accessed directly without
+inefficient indirect calls and other unnecessary API overhead.
+
+Signed-off-by: Eric Biggers <ebiggers@kernel.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 954d196bebb2 ("ksmbd: validate NTLMv2 response before updating session key")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/Kconfig      |    1 
+ fs/smb/server/auth.c       |  180 ++++++++++-----------------------------------
+ fs/smb/server/auth.h       |   10 +-
+ fs/smb/server/crypto_ctx.c |    8 --
+ fs/smb/server/crypto_ctx.h |    5 -
+ fs/smb/server/server.c     |    1 
+ fs/smb/server/smb2pdu.c    |   25 +-----
+ fs/smb/server/smb_common.h |    2 
+ 8 files changed, 54 insertions(+), 178 deletions(-)
+
+--- a/fs/smb/server/Kconfig
++++ b/fs/smb/server/Kconfig
+@@ -15,7 +15,6 @@ config SMB_SERVER
+       select CRYPTO_LIB_SHA256
+       select CRYPTO_LIB_SHA512
+       select CRYPTO_LIB_UTILS
+-      select CRYPTO_SHA256
+       select CRYPTO_CMAC
+       select CRYPTO_AEAD2
+       select CRYPTO_CCM
+--- a/fs/smb/server/auth.c
++++ b/fs/smb/server/auth.c
+@@ -592,46 +592,16 @@ int ksmbd_krb5_authenticate(struct ksmbd
+  * @sig:      signature value generated for client request packet
+  *
+  */
+-int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
+-                      int n_vec, char *sig)
++void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
++                       int n_vec, char *sig)
+ {
+-      struct ksmbd_crypto_ctx *ctx;
+-      int rc, i;
++      struct hmac_sha256_ctx ctx;
++      int i;
+-      ctx = ksmbd_crypto_ctx_find_hmacsha256();
+-      if (!ctx) {
+-              ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
+-              return -ENOMEM;
+-      }
+-
+-      rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx),
+-                               key,
+-                               SMB2_NTLMV2_SESSKEY_SIZE);
+-      if (rc)
+-              goto out;
+-
+-      rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx));
+-      if (rc) {
+-              ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc);
+-              goto out;
+-      }
+-
+-      for (i = 0; i < n_vec; i++) {
+-              rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
+-                                       iov[i].iov_base,
+-                                       iov[i].iov_len);
+-              if (rc) {
+-                      ksmbd_debug(AUTH, "hmacsha256 update error %d\n", rc);
+-                      goto out;
+-              }
+-      }
+-
+-      rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), sig);
+-      if (rc)
+-              ksmbd_debug(AUTH, "hmacsha256 generation error %d\n", rc);
+-out:
+-      ksmbd_release_crypto_ctx(ctx);
+-      return rc;
++      hmac_sha256_init_usingrawkey(&ctx, key, SMB2_NTLMV2_SESSKEY_SIZE);
++      for (i = 0; i < n_vec; i++)
++              hmac_sha256_update(&ctx, iov[i].iov_base, iov[i].iov_len);
++      hmac_sha256_final(&ctx, sig);
+ }
+ /**
+@@ -691,98 +661,39 @@ struct derivation {
+       bool binding;
+ };
+-static int generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
+-                      struct kvec label, struct kvec context, __u8 *key,
+-                      unsigned int key_size)
++static void generate_key(struct ksmbd_conn *conn, struct ksmbd_session *sess,
++                       struct kvec label, struct kvec context, __u8 *key,
++                       unsigned int key_size)
+ {
+       unsigned char zero = 0x0;
+       __u8 i[4] = {0, 0, 0, 1};
+       __u8 L128[4] = {0, 0, 0, 128};
+       __u8 L256[4] = {0, 0, 1, 0};
+-      int rc;
+       unsigned char prfhash[SMB2_HMACSHA256_SIZE];
+-      unsigned char *hashptr = prfhash;
+-      struct ksmbd_crypto_ctx *ctx;
+-
+-      memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
+-      memset(key, 0x0, key_size);
+-
+-      ctx = ksmbd_crypto_ctx_find_hmacsha256();
+-      if (!ctx) {
+-              ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
+-              return -ENOMEM;
+-      }
++      struct hmac_sha256_ctx ctx;
+-      rc = crypto_shash_setkey(CRYPTO_HMACSHA256_TFM(ctx),
+-                               sess->sess_key,
+-                               SMB2_NTLMV2_SESSKEY_SIZE);
+-      if (rc)
+-              goto smb3signkey_ret;
+-
+-      rc = crypto_shash_init(CRYPTO_HMACSHA256(ctx));
+-      if (rc) {
+-              ksmbd_debug(AUTH, "hmacsha256 init error %d\n", rc);
+-              goto smb3signkey_ret;
+-      }
+-
+-      rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), i, 4);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not update with n\n");
+-              goto smb3signkey_ret;
+-      }
+-
+-      rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
+-                               label.iov_base,
+-                               label.iov_len);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not update with label\n");
+-              goto smb3signkey_ret;
+-      }
+-
+-      rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), &zero, 1);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not update with zero\n");
+-              goto smb3signkey_ret;
+-      }
+-
+-      rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx),
+-                               context.iov_base,
+-                               context.iov_len);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not update with context\n");
+-              goto smb3signkey_ret;
+-      }
++      hmac_sha256_init_usingrawkey(&ctx, sess->sess_key,
++                                   SMB2_NTLMV2_SESSKEY_SIZE);
++      hmac_sha256_update(&ctx, i, 4);
++      hmac_sha256_update(&ctx, label.iov_base, label.iov_len);
++      hmac_sha256_update(&ctx, &zero, 1);
++      hmac_sha256_update(&ctx, context.iov_base, context.iov_len);
+       if (key_size == SMB3_ENC_DEC_KEY_SIZE &&
+           (conn->cipher_type == SMB2_ENCRYPTION_AES256_CCM ||
+            conn->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
+-              rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L256, 4);
++              hmac_sha256_update(&ctx, L256, 4);
+       else
+-              rc = crypto_shash_update(CRYPTO_HMACSHA256(ctx), L128, 4);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not update with L\n");
+-              goto smb3signkey_ret;
+-      }
+-
+-      rc = crypto_shash_final(CRYPTO_HMACSHA256(ctx), hashptr);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "Could not generate hmacmd5 hash error %d\n",
+-                          rc);
+-              goto smb3signkey_ret;
+-      }
++              hmac_sha256_update(&ctx, L128, 4);
+-      memcpy(key, hashptr, key_size);
+-
+-smb3signkey_ret:
+-      ksmbd_release_crypto_ctx(ctx);
+-      return rc;
++      hmac_sha256_final(&ctx, prfhash);
++      memcpy(key, prfhash, key_size);
+ }
+ static int generate_smb3signingkey(struct ksmbd_session *sess,
+                                  struct ksmbd_conn *conn,
+                                  const struct derivation *signing)
+ {
+-      int rc;
+       struct channel *chann;
+       char *key;
+@@ -795,10 +706,8 @@ static int generate_smb3signingkey(struc
+       else
+               key = sess->smb3signingkey;
+-      rc = generate_key(conn, sess, signing->label, signing->context, key,
+-                        SMB3_SIGN_KEY_SIZE);
+-      if (rc)
+-              return rc;
++      generate_key(conn, sess, signing->label, signing->context, key,
++                   SMB3_SIGN_KEY_SIZE);
+       if (!(conn->dialect >= SMB30_PROT_ID && signing->binding))
+               memcpy(chann->smb3signingkey, key, SMB3_SIGN_KEY_SIZE);
+@@ -850,32 +759,25 @@ struct derivation_twin {
+       struct derivation decryption;
+ };
+-static int generate_smb3encryptionkey(struct ksmbd_conn *conn,
+-                                    struct ksmbd_session *sess,
+-                                    const struct derivation_twin *ptwin)
+-{
+-      int rc;
+-
+-      rc = generate_key(conn, sess, ptwin->encryption.label,
+-                        ptwin->encryption.context, sess->smb3encryptionkey,
+-                        SMB3_ENC_DEC_KEY_SIZE);
+-      if (rc)
+-              return rc;
+-
+-      rc = generate_key(conn, sess, ptwin->decryption.label,
+-                        ptwin->decryption.context,
+-                        sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
+-      if (rc)
+-              return rc;
++static void generate_smb3encryptionkey(struct ksmbd_conn *conn,
++                                     struct ksmbd_session *sess,
++                                     const struct derivation_twin *ptwin)
++{
++      generate_key(conn, sess, ptwin->encryption.label,
++                   ptwin->encryption.context, sess->smb3encryptionkey,
++                   SMB3_ENC_DEC_KEY_SIZE);
++
++      generate_key(conn, sess, ptwin->decryption.label,
++                   ptwin->decryption.context,
++                   sess->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
+       ksmbd_debug(AUTH, "generated SMB3 encryption/decryption keys\n");
+       ksmbd_debug(AUTH, "Cipher type   %d\n", conn->cipher_type);
+       ksmbd_debug(AUTH, "Session Id    %llu\n", sess->id);
+-      return 0;
+ }
+-int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
+-                                struct ksmbd_session *sess)
++void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
++                                 struct ksmbd_session *sess)
+ {
+       struct derivation_twin twin;
+       struct derivation *d;
+@@ -892,11 +794,11 @@ int ksmbd_gen_smb30_encryptionkey(struct
+       d->context.iov_base = "ServerIn ";
+       d->context.iov_len = 10;
+-      return generate_smb3encryptionkey(conn, sess, &twin);
++      generate_smb3encryptionkey(conn, sess, &twin);
+ }
+-int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
+-                                 struct ksmbd_session *sess)
++void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
++                                  struct ksmbd_session *sess)
+ {
+       struct derivation_twin twin;
+       struct derivation *d;
+@@ -913,7 +815,7 @@ int ksmbd_gen_smb311_encryptionkey(struc
+       d->context.iov_base = sess->Preauth_HashValue;
+       d->context.iov_len = 64;
+-      return generate_smb3encryptionkey(conn, sess, &twin);
++      generate_smb3encryptionkey(conn, sess, &twin);
+ }
+ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf,
+--- a/fs/smb/server/auth.h
++++ b/fs/smb/server/auth.h
+@@ -52,18 +52,18 @@ ksmbd_build_ntlmssp_challenge_blob(struc
+                                  struct ksmbd_conn *conn);
+ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
+                           int in_len, char *out_blob, int *out_len);
+-int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
+-                      int n_vec, char *sig);
++void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
++                       int n_vec, char *sig);
+ int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
+                       int n_vec, char *sig);
+ int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess,
+                              struct ksmbd_conn *conn);
+ int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess,
+                               struct ksmbd_conn *conn);
+-int ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
+-                                struct ksmbd_session *sess);
+-int ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
++void ksmbd_gen_smb30_encryptionkey(struct ksmbd_conn *conn,
+                                  struct ksmbd_session *sess);
++void ksmbd_gen_smb311_encryptionkey(struct ksmbd_conn *conn,
++                                  struct ksmbd_session *sess);
+ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf,
+                                    __u8 *pi_hash);
+ #endif
+--- a/fs/smb/server/crypto_ctx.c
++++ b/fs/smb/server/crypto_ctx.c
+@@ -69,9 +69,6 @@ static struct shash_desc *alloc_shash_de
+       case CRYPTO_SHASH_HMACMD5:
+               tfm = crypto_alloc_shash("hmac(md5)", 0, 0);
+               break;
+-      case CRYPTO_SHASH_HMACSHA256:
+-              tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
+-              break;
+       case CRYPTO_SHASH_CMACAES:
+               tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
+               break;
+@@ -182,11 +179,6 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ct
+       return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5);
+ }
+-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void)
+-{
+-      return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256);
+-}
+-
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
+ {
+       return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
+--- a/fs/smb/server/crypto_ctx.h
++++ b/fs/smb/server/crypto_ctx.h
+@@ -11,7 +11,6 @@
+ enum {
+       CRYPTO_SHASH_HMACMD5    = 0,
+-      CRYPTO_SHASH_HMACSHA256,
+       CRYPTO_SHASH_CMACAES,
+       CRYPTO_SHASH_MAX,
+ };
+@@ -35,12 +34,9 @@ struct ksmbd_crypto_ctx {
+ };
+ #define CRYPTO_HMACMD5(c)     ((c)->desc[CRYPTO_SHASH_HMACMD5])
+-#define CRYPTO_HMACSHA256(c)  ((c)->desc[CRYPTO_SHASH_HMACSHA256])
+ #define CRYPTO_CMACAES(c)     ((c)->desc[CRYPTO_SHASH_CMACAES])
+ #define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm)
+-#define CRYPTO_HMACSHA256_TFM(c)\
+-                              ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm)
+ #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
+ #define CRYPTO_GCM(c)         ((c)->ccmaes[CRYPTO_AEAD_AES_GCM])
+@@ -48,7 +44,6 @@ struct ksmbd_crypto_ctx {
+ void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void);
+-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -639,7 +639,6 @@ MODULE_SOFTDEP("pre: md5");
+ MODULE_SOFTDEP("pre: nls");
+ MODULE_SOFTDEP("pre: aes");
+ MODULE_SOFTDEP("pre: cmac");
+-MODULE_SOFTDEP("pre: sha256");
+ MODULE_SOFTDEP("pre: aead2");
+ MODULE_SOFTDEP("pre: ccm");
+ MODULE_SOFTDEP("pre: gcm");
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -1552,12 +1552,7 @@ static int ntlm_authenticate(struct ksmb
+       if (smb3_encryption_negotiated(conn) &&
+                       !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
+-              rc = conn->ops->generate_encryptionkey(conn, sess);
+-              if (rc) {
+-                      ksmbd_debug(SMB,
+-                                      "SMB3 encryption key generation failed\n");
+-                      return -EINVAL;
+-              }
++              conn->ops->generate_encryptionkey(conn, sess);
+               sess->enc = true;
+               if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
+                       rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
+@@ -1656,12 +1651,7 @@ static int krb5_authenticate(struct ksmb
+       if (smb3_encryption_negotiated(conn) &&
+           !(req->Flags & SMB2_SESSION_REQ_FLAG_BINDING)) {
+-              retval = conn->ops->generate_encryptionkey(conn, sess);
+-              if (retval) {
+-                      ksmbd_debug(SMB,
+-                                  "SMB3 encryption key generation failed\n");
+-                      return -EINVAL;
+-              }
++              conn->ops->generate_encryptionkey(conn, sess);
+               sess->enc = true;
+               if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION)
+                       rsp->SessionFlags = SMB2_SESSION_FLAG_ENCRYPT_DATA_LE;
+@@ -9077,9 +9067,8 @@ int smb2_check_sign_req(struct ksmbd_wor
+       iov[0].iov_base = (char *)&hdr->ProtocolId;
+       iov[0].iov_len = len;
+-      if (ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1,
+-                              signature))
+-              return 0;
++      ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, 1,
++                          signature);
+       if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
+               pr_err("bad smb2 signature\n");
+@@ -9112,9 +9101,9 @@ void smb2_set_sign_rsp(struct ksmbd_work
+               iov = &work->iov[work->iov_idx];
+       }
+-      if (!ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec,
+-                               signature))
+-              memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
++      ksmbd_sign_smb2_pdu(work->conn, work->sess->sess_key, iov, n_vec,
++                          signature);
++      memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
+ }
+ /**
+--- a/fs/smb/server/smb_common.h
++++ b/fs/smb/server/smb_common.h
+@@ -373,7 +373,7 @@ struct smb_version_ops {
+       int (*check_sign_req)(struct ksmbd_work *work);
+       void (*set_sign_rsp)(struct ksmbd_work *work);
+       int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn);
+-      int (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess);
++      void (*generate_encryptionkey)(struct ksmbd_conn *conn, struct ksmbd_session *sess);
+       bool (*is_transform_hdr)(void *buf);
+       int (*decrypt_req)(struct ksmbd_work *work);
+       int (*encrypt_resp)(struct ksmbd_work *work);
diff --git a/queue-6.18/ksmbd-use-opener-credentials-for-fsctl-mutations.patch b/queue-6.18/ksmbd-use-opener-credentials-for-fsctl-mutations.patch
new file mode 100644 (file)
index 0000000..ee9bbcb
--- /dev/null
@@ -0,0 +1,86 @@
+From stable+bounces-275228-greg=kroah.com@vger.kernel.org Thu Jul 16 12:23:59 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 06:18:19 -0400
+Subject: ksmbd: use opener credentials for FSCTL mutations
+To: stable@vger.kernel.org
+Cc: Namjae Jeon <linkinjeon@kernel.org>, Musaab Khan <musaab.khan@protonmail.com>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716101819.1461638-3-sashal@kernel.org>
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+[ Upstream commit c6394bcaf254c5baf9aff43376020be5db6d3316 ]
+
+SET_SPARSE, SET_ZERO_DATA and SET_COMPRESSION operate on an open SMB
+handle but call VFS xattr, fallocate or fileattr helpers with the current
+ksmbd worker credentials. Those helpers can revalidate inode permissions,
+ownership and LSM policy independently of the SMB handle access mask.
+
+Run each operation with the credentials captured in the target file when
+the handle was opened. Keep credential handling local to these single-file
+FSCTLs rather than applying session credentials to the complete IOCTL
+handler, which also contains handle-less and multi-handle operations.
+
+Cc: stable@vger.kernel.org
+Reported-by: Musaab Khan <musaab.khan@protonmail.com>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/smb2pdu.c |    3 +++
+ fs/smb/server/vfs.c     |   20 +++++++++++++-------
+ 2 files changed, 16 insertions(+), 7 deletions(-)
+
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -8273,6 +8273,7 @@ static inline int fsctl_set_sparse(struc
+       if (fp->f_ci->m_fattr != old_fattr &&
+           test_share_config_flag(work->tcon->share_conf,
+                                  KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) {
++              const struct cred *saved_cred;
+               struct xattr_dos_attrib da;
+               ret = ksmbd_vfs_get_dos_attrib_xattr(idmap,
+@@ -8281,9 +8282,11 @@ static inline int fsctl_set_sparse(struc
+                       goto out;
+               da.attr = le32_to_cpu(fp->f_ci->m_fattr);
++              saved_cred = override_creds(fp->filp->f_cred);
+               ret = ksmbd_vfs_set_dos_attrib_xattr(idmap,
+                                                    &fp->filp->f_path,
+                                                    &da, true);
++              revert_creds(saved_cred);
+               if (ret)
+                       fp->f_ci->m_fattr = old_fattr;
+       }
+--- a/fs/smb/server/vfs.c
++++ b/fs/smb/server/vfs.c
+@@ -995,15 +995,21 @@ void ksmbd_vfs_set_fadvise(struct file *
+ int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
+                       loff_t off, loff_t len)
+ {
++      const struct cred *saved_cred;
++      int err;
++
+       smb_break_all_levII_oplock(work, fp, 1);
++      saved_cred = override_creds(fp->filp->f_cred);
+       if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE)
+-              return vfs_fallocate(fp->filp,
+-                                   FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+-                                   off, len);
+-
+-      return vfs_fallocate(fp->filp,
+-                           FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE,
+-                           off, len);
++              err = vfs_fallocate(fp->filp,
++                                  FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
++                                  off, len);
++      else
++              err = vfs_fallocate(fp->filp,
++                                  FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE,
++                                  off, len);
++      revert_creds(saved_cred);
++      return err;
+ }
+ int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
diff --git a/queue-6.18/ksmbd-use-sha-512-library-for-smb3.1.1-preauth-hash.patch b/queue-6.18/ksmbd-use-sha-512-library-for-smb3.1.1-preauth-hash.patch
new file mode 100644 (file)
index 0000000..5f66f7b
--- /dev/null
@@ -0,0 +1,177 @@
+From stable+bounces-275251-greg=kroah.com@vger.kernel.org Thu Jul 16 13:22:06 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 07:21:44 -0400
+Subject: ksmbd: Use SHA-512 library for SMB3.1.1 preauth hash
+To: stable@vger.kernel.org
+Cc: Eric Biggers <ebiggers@kernel.org>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716112147.1735816-1-sashal@kernel.org>
+
+From: Eric Biggers <ebiggers@kernel.org>
+
+[ Upstream commit e009cb1e3093977c5b96a1c6697a7dc9332222d5 ]
+
+Convert ksmbd_gen_preauth_integrity_hash() to use the SHA-512 library
+instead of a "sha512" crypto_shash.  This is simpler and faster.  With
+the library there's no need to allocate memory, no need to handle
+errors, and the SHA-512 code is accessed directly without inefficient
+indirect calls and other unnecessary API overhead.
+
+Signed-off-by: Eric Biggers <ebiggers@kernel.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 954d196bebb2 ("ksmbd: validate NTLMv2 response before updating session key")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/Kconfig      |    2 +-
+ fs/smb/server/auth.c       |   41 +++++++----------------------------------
+ fs/smb/server/crypto_ctx.c |    8 --------
+ fs/smb/server/crypto_ctx.h |    4 ----
+ fs/smb/server/server.c     |    1 -
+ 5 files changed, 8 insertions(+), 48 deletions(-)
+
+--- a/fs/smb/server/Kconfig
++++ b/fs/smb/server/Kconfig
+@@ -13,10 +13,10 @@ config SMB_SERVER
+       select CRYPTO_LIB_ARC4
+       select CRYPTO_LIB_DES
+       select CRYPTO_LIB_SHA256
++      select CRYPTO_LIB_SHA512
+       select CRYPTO_LIB_UTILS
+       select CRYPTO_SHA256
+       select CRYPTO_CMAC
+-      select CRYPTO_SHA512
+       select CRYPTO_AEAD2
+       select CRYPTO_CCM
+       select CRYPTO_GCM
+--- a/fs/smb/server/auth.c
++++ b/fs/smb/server/auth.c
+@@ -13,6 +13,7 @@
+ #include <linux/xattr.h>
+ #include <crypto/hash.h>
+ #include <crypto/aead.h>
++#include <crypto/sha2.h>
+ #include <crypto/utils.h>
+ #include <linux/random.h>
+ #include <linux/scatterlist.h>
+@@ -918,48 +919,20 @@ int ksmbd_gen_smb311_encryptionkey(struc
+ int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf,
+                                    __u8 *pi_hash)
+ {
+-      int rc;
+       struct smb2_hdr *rcv_hdr = smb2_get_msg(buf);
+       char *all_bytes_msg = (char *)&rcv_hdr->ProtocolId;
+       int msg_size = get_rfc1002_len(buf);
+-      struct ksmbd_crypto_ctx *ctx = NULL;
++      struct sha512_ctx sha_ctx;
+       if (conn->preauth_info->Preauth_HashId !=
+           SMB2_PREAUTH_INTEGRITY_SHA512)
+               return -EINVAL;
+-      ctx = ksmbd_crypto_ctx_find_sha512();
+-      if (!ctx) {
+-              ksmbd_debug(AUTH, "could not alloc sha512\n");
+-              return -ENOMEM;
+-      }
+-
+-      rc = crypto_shash_init(CRYPTO_SHA512(ctx));
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not init shashn");
+-              goto out;
+-      }
+-
+-      rc = crypto_shash_update(CRYPTO_SHA512(ctx), pi_hash, 64);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not update with n\n");
+-              goto out;
+-      }
+-
+-      rc = crypto_shash_update(CRYPTO_SHA512(ctx), all_bytes_msg, msg_size);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "could not update with n\n");
+-              goto out;
+-      }
+-
+-      rc = crypto_shash_final(CRYPTO_SHA512(ctx), pi_hash);
+-      if (rc) {
+-              ksmbd_debug(AUTH, "Could not generate hash err : %d\n", rc);
+-              goto out;
+-      }
+-out:
+-      ksmbd_release_crypto_ctx(ctx);
+-      return rc;
++      sha512_init(&sha_ctx);
++      sha512_update(&sha_ctx, pi_hash, 64);
++      sha512_update(&sha_ctx, all_bytes_msg, msg_size);
++      sha512_final(&sha_ctx, pi_hash);
++      return 0;
+ }
+ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,
+--- a/fs/smb/server/crypto_ctx.c
++++ b/fs/smb/server/crypto_ctx.c
+@@ -75,9 +75,6 @@ static struct shash_desc *alloc_shash_de
+       case CRYPTO_SHASH_CMACAES:
+               tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
+               break;
+-      case CRYPTO_SHASH_SHA512:
+-              tfm = crypto_alloc_shash("sha512", 0, 0);
+-              break;
+       default:
+               return NULL;
+       }
+@@ -195,11 +192,6 @@ struct ksmbd_crypto_ctx *ksmbd_crypto_ct
+       return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
+ }
+-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void)
+-{
+-      return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512);
+-}
+-
+ static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
+ {
+       struct ksmbd_crypto_ctx *ctx;
+--- a/fs/smb/server/crypto_ctx.h
++++ b/fs/smb/server/crypto_ctx.h
+@@ -13,7 +13,6 @@ enum {
+       CRYPTO_SHASH_HMACMD5    = 0,
+       CRYPTO_SHASH_HMACSHA256,
+       CRYPTO_SHASH_CMACAES,
+-      CRYPTO_SHASH_SHA512,
+       CRYPTO_SHASH_MAX,
+ };
+@@ -38,13 +37,11 @@ struct ksmbd_crypto_ctx {
+ #define CRYPTO_HMACMD5(c)     ((c)->desc[CRYPTO_SHASH_HMACMD5])
+ #define CRYPTO_HMACSHA256(c)  ((c)->desc[CRYPTO_SHASH_HMACSHA256])
+ #define CRYPTO_CMACAES(c)     ((c)->desc[CRYPTO_SHASH_CMACAES])
+-#define CRYPTO_SHA512(c)      ((c)->desc[CRYPTO_SHASH_SHA512])
+ #define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm)
+ #define CRYPTO_HMACSHA256_TFM(c)\
+                               ((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm)
+ #define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
+-#define CRYPTO_SHA512_TFM(c)  ((c)->desc[CRYPTO_SHASH_SHA512]->tfm)
+ #define CRYPTO_GCM(c)         ((c)->ccmaes[CRYPTO_AEAD_AES_GCM])
+ #define CRYPTO_CCM(c)         ((c)->ccmaes[CRYPTO_AEAD_AES_CCM])
+@@ -53,7 +50,6 @@ void ksmbd_release_crypto_ctx(struct ksm
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
+-struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
+ struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
+ void ksmbd_crypto_destroy(void);
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -640,7 +640,6 @@ MODULE_SOFTDEP("pre: nls");
+ MODULE_SOFTDEP("pre: aes");
+ MODULE_SOFTDEP("pre: cmac");
+ MODULE_SOFTDEP("pre: sha256");
+-MODULE_SOFTDEP("pre: sha512");
+ MODULE_SOFTDEP("pre: aead2");
+ MODULE_SOFTDEP("pre: ccm");
+ MODULE_SOFTDEP("pre: gcm");
diff --git a/queue-6.18/ksmbd-validate-ntlmv2-response-before-updating-session-key.patch b/queue-6.18/ksmbd-validate-ntlmv2-response-before-updating-session-key.patch
new file mode 100644 (file)
index 0000000..6b99b10
--- /dev/null
@@ -0,0 +1,100 @@
+From stable+bounces-275254-greg=kroah.com@vger.kernel.org Thu Jul 16 13:22:31 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 07:21:47 -0400
+Subject: ksmbd: validate NTLMv2 response before updating session key
+To: stable@vger.kernel.org
+Cc: Haofeng Li <lihaofeng@kylinos.cn>, ChenXiaoSong <chenxiaosong@kylinos.cn>, Namjae Jeon <linkinjeon@kernel.org>, Steve French <stfrench@microsoft.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716112147.1735816-4-sashal@kernel.org>
+
+From: Haofeng Li <lihaofeng@kylinos.cn>
+
+[ Upstream commit 954d196bebb2b50151cb96454c72dc113b2af1ac ]
+
+ksmbd_auth_ntlmv2() derives the NTLMv2 session key into
+sess->sess_key before it verifies the NTLMv2 response.
+ksmbd_decode_ntlmssp_auth_blob() then continues into KEY_XCH even
+when ksmbd_auth_ntlmv2() failed.
+
+With SMB3 multichannel binding, the failed authentication operates on
+an existing session and the session setup error path does not expire
+binding sessions. A client can send a binding session setup with a
+bad NT proof and KEY_XCH and still modify sess->sess_key before
+STATUS_LOGON_FAILURE is returned.
+
+Relevant path:
+
+  smb2_sess_setup()
+    -> conn->binding = true
+    -> ntlm_authenticate()
+       -> session_user()
+       -> ksmbd_decode_ntlmssp_auth_blob()
+          -> ksmbd_auth_ntlmv2()
+             -> calc_ntlmv2_hash()
+             -> hmac_md5_usingrawkey(..., sess->sess_key)
+             -> crypto_memneq() returns mismatch
+          -> KEY_XCH arc4_crypt(..., sess->sess_key, ...)
+    -> out_err without expiring the binding session
+
+Derive the base session key into a local buffer and copy it to
+sess->sess_key only after the proof matches. Return immediately on
+authentication failure so KEY_XCH is only processed after successful
+authentication.
+
+Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
+Fixes: f9929ef6a2a5 ("ksmbd: add support for key exchange")
+Cc: stable@vger.kernel.org
+Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn>
+Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/auth.c |   20 ++++++++++++++++----
+ 1 file changed, 16 insertions(+), 4 deletions(-)
+
+--- a/fs/smb/server/auth.c
++++ b/fs/smb/server/auth.c
+@@ -142,6 +142,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn
+ {
+       char ntlmv2_hash[CIFS_ENCPWD_SIZE];
+       char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
++      char sess_key[SMB2_NTLMV2_SESSKEY_SIZE];
+       struct hmac_md5_ctx ctx;
+       int rc;
+@@ -164,12 +165,21 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn
+       /* Generate the session key */
+       hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE,
+                            ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE,
+-                           sess->sess_key);
++                           sess_key);
+       if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp,
+-                        CIFS_HMAC_MD5_HASH_SIZE))
+-              return -EINVAL;
+-      return 0;
++                        CIFS_HMAC_MD5_HASH_SIZE)) {
++              rc = -EINVAL;
++              goto out;
++      }
++
++      memcpy(sess->sess_key, sess_key, sizeof(sess_key));
++      rc = 0;
++out:
++      memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash));
++      memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp));
++      memzero_explicit(sess_key, sizeof(sess_key));
++      return rc;
+ }
+ /**
+@@ -226,6 +236,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struc
+                               nt_len - CIFS_ENCPWD_SIZE,
+                               domain_name, conn->ntlmssp.cryptkey);
+       kfree(domain_name);
++      if (ret)
++              return ret;
+       /* The recovered secondary session key */
+       if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) {
diff --git a/queue-6.18/media-nxp-imx8-isi-fix-use-after-free-on-remove.patch b/queue-6.18/media-nxp-imx8-isi-fix-use-after-free-on-remove.patch
new file mode 100644 (file)
index 0000000..a959944
--- /dev/null
@@ -0,0 +1,73 @@
+From stable+bounces-274914-greg=kroah.com@vger.kernel.org Wed Jul 15 13:44:01 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 07:40:28 -0400
+Subject: media: nxp: imx8-isi: Fix use-after-free on remove
+To: stable@vger.kernel.org
+Cc: Xiaolei Wang <xiaolei.wang@windriver.com>, Frank Li <Frank.Li@nxp.com>, Laurent Pinchart <laurent.pinchart@ideasonboard.com>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715114028.727360-2-sashal@kernel.org>
+
+From: Xiaolei Wang <xiaolei.wang@windriver.com>
+
+[ Upstream commit b670bf89824ede5d07d20bb9bfbafb754846081d ]
+
+KASAN reports a slab-use-after-free in __media_entity_remove_link()
+during rmmod of imx8_isi:
+
+  BUG: KASAN: slab-use-after-free in __media_entity_remove_link+0x608/0x650
+  Read of size 2 at addr ffff0000d47cb02a by task rmmod/724
+
+  Call trace:
+   __media_entity_remove_link+0x608/0x650
+   __media_entity_remove_links+0x78/0x144
+   __media_device_unregister_entity+0x150/0x280
+   media_device_unregister_entity+0x48/0x68
+   v4l2_device_unregister_subdev+0x158/0x300
+   v4l2_async_unbind_subdev_one+0x22c/0x358
+   v4l2_async_nf_unbind_all_subdevs+0xfc/0x1c0
+   v4l2_async_nf_unregister+0x5c/0x14c
+   mxc_isi_remove+0x124/0x2a0 [imx8_isi]
+
+  Allocated by task 249:
+   __kmalloc_noprof+0x27c/0x690
+   mxc_isi_crossbar_init+0x22c/0x560 [imx8_isi]
+
+  Freed by task 724:
+   kfree+0x1e4/0x5b0
+   mxc_isi_crossbar_cleanup+0x34/0x80 [imx8_isi]
+   mxc_isi_remove+0x11c/0x2a0 [imx8_isi]
+
+The problem is that mxc_isi_remove() calls mxc_isi_crossbar_cleanup()
+before mxc_isi_v4l2_cleanup(). The crossbar cleanup frees the media
+entity pads, but the subsequent v4l2 cleanup still tries to remove
+media links that reference those pads.
+
+Fix this by calling mxc_isi_v4l2_cleanup() before
+mxc_isi_crossbar_cleanup() to ensure all media entities are properly
+unregistered while the pads are still valid.
+
+Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver")
+Cc: stable@vger.kernel.org
+Signed-off-by: Xiaolei Wang <xiaolei.wang@windriver.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Link: https://patch.msgid.link/20260507041318.491594-2-xiaolei.wang@windriver.com
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
++++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+@@ -535,8 +535,8 @@ static void mxc_isi_remove(struct platfo
+               mxc_isi_pipe_cleanup(pipe);
+       }
+-      mxc_isi_crossbar_cleanup(&isi->crossbar);
+       mxc_isi_v4l2_cleanup(isi);
++      mxc_isi_crossbar_cleanup(&isi->crossbar);
+ }
+ static const struct of_device_id mxc_isi_of_match[] = {
diff --git a/queue-6.18/media-nxp-imx8-isi-use-devm_pm_runtime_enable-to-simplify-code.patch b/queue-6.18/media-nxp-imx8-isi-use-devm_pm_runtime_enable-to-simplify-code.patch
new file mode 100644 (file)
index 0000000..50ff517
--- /dev/null
@@ -0,0 +1,70 @@
+From stable+bounces-274913-greg=kroah.com@vger.kernel.org Wed Jul 15 13:43:57 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 15 Jul 2026 07:40:27 -0400
+Subject: media: nxp: imx8-isi: use devm_pm_runtime_enable() to simplify code
+To: stable@vger.kernel.org
+Cc: Frank Li <Frank.Li@nxp.com>, Laurent Pinchart <laurent.pinchart@ideasonboard.com>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715114028.727360-1-sashal@kernel.org>
+
+From: Frank Li <Frank.Li@nxp.com>
+
+[ Upstream commit 078161dd44d6f848a62a473206d69025607736ec ]
+
+Use devm_pm_runtime_enable() to simplify code. Change to use
+dev_err_probe() because previous goto change to return.
+
+No functional change.
+
+Signed-off-by: Frank Li <Frank.Li@nxp.com>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Link: https://patch.msgid.link/20260116-cam_cleanup-v4-2-29ce01640443@nxp.com
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Stable-dep-of: b670bf89824e ("media: nxp: imx8-isi: Fix use-after-free on remove")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c |   16 +++++++---------
+ 1 file changed, 7 insertions(+), 9 deletions(-)
+
+--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
++++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+@@ -488,13 +488,14 @@ static int mxc_isi_probe(struct platform
+       dma_size = isi->pdata->has_36bit_dma ? 36 : 32;
+       dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));
+-      pm_runtime_enable(dev);
++      ret = devm_pm_runtime_enable(dev);
++      if (ret)
++              return ret;
+       ret = mxc_isi_crossbar_init(isi);
+-      if (ret) {
+-              dev_err(dev, "Failed to initialize crossbar: %d\n", ret);
+-              goto err_pm;
+-      }
++      if (ret)
++              return dev_err_probe(dev, ret,
++                                   "Failed to initialize crossbar\n");
+       for (i = 0; i < isi->pdata->num_channels; ++i) {
+               ret = mxc_isi_pipe_init(isi, i);
+@@ -517,8 +518,7 @@ static int mxc_isi_probe(struct platform
+ err_xbar:
+       mxc_isi_crossbar_cleanup(&isi->crossbar);
+-err_pm:
+-      pm_runtime_disable(isi->dev);
++
+       return ret;
+ }
+@@ -537,8 +537,6 @@ static void mxc_isi_remove(struct platfo
+       mxc_isi_crossbar_cleanup(&isi->crossbar);
+       mxc_isi_v4l2_cleanup(isi);
+-
+-      pm_runtime_disable(isi->dev);
+ }
+ static const struct of_device_id mxc_isi_of_match[] = {
diff --git a/queue-6.18/new-helper-simple_remove_by_name.patch b/queue-6.18/new-helper-simple_remove_by_name.patch
new file mode 100644 (file)
index 0000000..20b7681
--- /dev/null
@@ -0,0 +1,85 @@
+From stable+bounces-277541-greg=kroah.com@vger.kernel.org Mon Jul 20 04:36:03 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:46 -0400
+Subject: new helper: simple_remove_by_name()
+To: stable@vger.kernel.org
+Cc: Al Viro <viro@zeniv.linux.org.uk>, Miklos Szeredi <mszeredi@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720023553.2928009-1-sashal@kernel.org>
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+[ Upstream commit 4051a9115ad24bb9a691774730ca9c1dd56de665 ]
+
+simple_recursive_removal(), but instead of victim dentry it takes
+parent + name.
+
+Used to be open-coded in fs/fuse/control.c, but there's no need to expose
+the guts of that thing there and there are other potential users, so
+let's lift it into libfs...
+
+Acked-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Stable-dep-of: 3137b243c939 ("usb: gadget: f_fs: initialize reset_work at allocation time")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/fuse/control.c  |    7 +------
+ fs/libfs.c         |   13 +++++++++++++
+ include/linux/fs.h |    2 ++
+ 3 files changed, 16 insertions(+), 6 deletions(-)
+
+--- a/fs/fuse/control.c
++++ b/fs/fuse/control.c
+@@ -290,18 +290,13 @@ static void remove_one(struct dentry *de
+  */
+ void fuse_ctl_remove_conn(struct fuse_conn *fc)
+ {
+-      struct dentry *dentry;
+       char name[32];
+       if (!fuse_control_sb || fc->no_control)
+               return;
+       sprintf(name, "%u", fc->dev);
+-      dentry = lookup_noperm_positive_unlocked(&QSTR(name), fuse_control_sb->s_root);
+-      if (!IS_ERR(dentry)) {
+-              simple_recursive_removal(dentry, remove_one);
+-              dput(dentry);   // paired with lookup_noperm_positive_unlocked()
+-      }
++      simple_remove_by_name(fuse_control_sb->s_root, name, remove_one);
+ }
+ static int fuse_ctl_fill_super(struct super_block *sb, struct fs_context *fsc)
+--- a/fs/libfs.c
++++ b/fs/libfs.c
+@@ -647,6 +647,19 @@ void simple_recursive_removal(struct den
+ }
+ EXPORT_SYMBOL(simple_recursive_removal);
++void simple_remove_by_name(struct dentry *parent, const char *name,
++                           void (*callback)(struct dentry *))
++{
++      struct dentry *dentry;
++
++      dentry = lookup_noperm_positive_unlocked(&QSTR(name), parent);
++      if (!IS_ERR(dentry)) {
++              simple_recursive_removal(dentry, callback);
++              dput(dentry);   // paired with lookup_noperm_positive_unlocked()
++      }
++}
++EXPORT_SYMBOL(simple_remove_by_name);
++
+ /* caller holds parent directory with I_MUTEX_PARENT */
+ void locked_recursive_removal(struct dentry *dentry,
+                               void (*callback)(struct dentry *))
+--- a/include/linux/fs.h
++++ b/include/linux/fs.h
+@@ -3655,6 +3655,8 @@ extern int simple_rename(struct mnt_idma
+                        unsigned int);
+ extern void simple_recursive_removal(struct dentry *,
+                               void (*callback)(struct dentry *));
++extern void simple_remove_by_name(struct dentry *, const char *,
++                              void (*callback)(struct dentry *));
+ extern void locked_recursive_removal(struct dentry *,
+                               void (*callback)(struct dentry *));
+ extern int noop_fsync(struct file *, loff_t, loff_t, int);
diff --git a/queue-6.18/pci-add-kerneldoc-for-pci_resize_resource.patch b/queue-6.18/pci-add-kerneldoc-for-pci_resize_resource.patch
new file mode 100644 (file)
index 0000000..01e560c
--- /dev/null
@@ -0,0 +1,55 @@
+From stable+bounces-274549-greg=kroah.com@vger.kernel.org Tue Jul 14 22:04:10 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:34 -0400
+Subject: PCI: Add kerneldoc for pci_resize_resource()
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Alex Bennée" <alex.bennee@linaro.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714200236.3153778-6-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit d787018e2dfdc4c1331538e7a8717690d1b7c9b3 ]
+
+As pci_resize_resource() is meant to be used also outside of PCI core,
+document the interface with kerneldoc.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Link: https://patch.msgid.link/20251113162628.5946-8-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/setup-res.c |   20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -457,6 +457,26 @@ void pci_resize_resource_set_size(struct
+       resource_set_size(res, res_size);
+ }
++/**
++ * pci_resize_resource - reconfigure a Resizable BAR and resources
++ * @dev: the PCI device
++ * @resno: index of the BAR to be resized
++ * @size: new size as defined in the spec (0=1MB, 31=128TB)
++ * @exclude_bars: a mask of BARs that should not be released
++ *
++ * Reconfigure @resno to @size and re-run resource assignment algorithm
++ * with the new size.
++ *
++ * Prior to resize, release @dev resources that share a bridge window with
++ * @resno.  This unpins the bridge window resource to allow changing it.
++ *
++ * The caller may prevent releasing a particular BAR by providing
++ * @exclude_bars mask, but this may result in the resize operation failing
++ * due to insufficient space.
++ *
++ * Return: 0 on success, or negative on error. In case of an error, the
++ *         resources are restored to their original places.
++ */
+ int pci_resize_resource(struct pci_dev *dev, int resno, int size,
+                       int exclude_bars)
+ {
diff --git a/queue-6.18/pci-change-pci_dev-variable-from-bridge-to-dev.patch b/queue-6.18/pci-change-pci_dev-variable-from-bridge-to-dev.patch
new file mode 100644 (file)
index 0000000..e19b1aa
--- /dev/null
@@ -0,0 +1,82 @@
+From stable+bounces-274544-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:55 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:30 -0400
+Subject: PCI: Change pci_dev variable from 'bridge' to 'dev'
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Alex Bennée" <alex.bennee@linaro.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714200236.3153778-2-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 34c702ea0497e092a487eacdf28a037f43e3e39f ]
+
+Upcoming fix to BAR resize will store also device BAR resource in the
+saved list. Change the pci_dev variable in the loop from 'bridge' to
+'dev' as the former would be misleading with non-bridges in the list.
+
+This is in a separate change to reduce churn in the upcoming BAR resize
+fix.
+
+While it appears that the logic in the loop doing pci_setup_bridge() is
+altered as 'bridge' variable is no longer updated, a bridge should never
+appear more than once in the saved list so the check can only match to the
+first entry. As such, the code with two distinct pci_dev variables better
+represents the intention of the check compared with the old code where
+bridge variable was reused for a different purpose.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
+Link: https://patch.msgid.link/20251113162628.5946-4-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/setup-bus.c |   17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -2431,12 +2431,13 @@ int pbus_reassign_bridge_resources(struc
+       }
+       list_for_each_entry(dev_res, &saved, list) {
++              struct pci_dev *dev = dev_res->dev;
++
+               /* Skip the bridge we just assigned resources for */
+-              if (bridge == dev_res->dev)
++              if (bridge == dev)
+                       continue;
+-              bridge = dev_res->dev;
+-              pci_setup_bridge(bridge->subordinate);
++              pci_setup_bridge(dev->subordinate);
+       }
+       free_list(&saved);
+@@ -2452,19 +2453,19 @@ cleanup:
+       /* Revert to the old configuration */
+       list_for_each_entry(dev_res, &saved, list) {
+               struct resource *res = dev_res->res;
++              struct pci_dev *dev = dev_res->dev;
+-              bridge = dev_res->dev;
+-              i = pci_resource_num(bridge, res);
++              i = pci_resource_num(dev, res);
+               if (res->parent) {
+                       release_child_resources(res);
+-                      pci_release_resource(bridge, i);
++                      pci_release_resource(dev, i);
+               }
+               restore_dev_resource(dev_res);
+-              pci_claim_resource(bridge, i);
+-              pci_setup_bridge(bridge->subordinate);
++              pci_claim_resource(dev, i);
++              pci_setup_bridge(dev->subordinate);
+       }
+       free_list(&saved);
+       up_read(&pci_bus_sem);
diff --git a/queue-6.18/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch b/queue-6.18/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch
new file mode 100644 (file)
index 0000000..1b088e1
--- /dev/null
@@ -0,0 +1,393 @@
+From stable+bounces-274550-greg=kroah.com@vger.kernel.org Tue Jul 14 22:04:21 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:33 -0400
+Subject: PCI: Fix restoring BARs on BAR resize rollback path
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Simon Richter" <Simon.Richter@hogyros.de>, "Alex Bennée" <alex.bennee@linaro.org>, "Bjorn Helgaas" <bhelgaas@google.com>, "Christian König" <christian.koenig@amd.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714200236.3153778-5-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 337b1b566db087347194e4543ddfdfa5645275cc ]
+
+BAR resize operation is implemented in the pci_resize_resource() and
+pbus_reassign_bridge_resources() functions. pci_resize_resource() can be
+called either from __resource_resize_store() from sysfs or directly by the
+driver for the Endpoint Device.
+
+The pci_resize_resource() requires that caller has released the device
+resources that share the bridge window with the BAR to be resized as
+otherwise the bridge window is pinned in place and cannot be changed.
+
+pbus_reassign_bridge_resources() rolls back resources if the resize
+operation fails, but rollback is performed only for the bridge windows.
+Because releasing the device resources are done by the caller of the BAR
+resize interface, these functions performing the BAR resize do not have
+access to the device resources as they were before the resize.
+
+pbus_reassign_bridge_resources() could try __pci_bridge_assign_resources()
+after rolling back the bridge windows as they were, however, it will not
+guarantee the resource are assigned due to differences in how FW and the
+kernel assign the resources (alignment of the start address and tail).
+
+To perform rollback robustly, the BAR resize interface has to be altered to
+also release the device resources that share the bridge window with the BAR
+to be resized.
+
+Also, remove restoring from the entries failed list as saved list should
+now contain both the bridge windows and device resources so the extra
+restore is duplicated work.
+
+Some drivers (currently only amdgpu) want to prevent releasing some
+resources. Add exclude_bars param to pci_resize_resource() and make amdgpu
+pass its register BAR (BAR 2 or 5), which should never be released during
+resize operation. Normally 64-bit prefetchable resources do not share a
+bridge window with the 32-bit only register BAR, but there are various
+fallbacks in the resource assignment logic which may make the resources
+share the bridge window in rare cases.
+
+This change (together with the driver side changes) is to counter the
+resource releases that had to be done to prevent resource tree corruption
+in the ("PCI: Release assigned resource before restoring them") change. As
+such, it likely restores functionality in cases where device resources were
+released to avoid resource tree conflicts which appeared to be "working"
+when such conflicts were not correctly detected by the kernel.
+
+Reported-by: Simon Richter <Simon.Richter@hogyros.de>
+Link: https://lore.kernel.org/linux-pci/f9a8c975-f5d3-4dd2-988e-4371a1433a60@hogyros.de/
+Reported-by: Alex Bennée <alex.bennee@linaro.org>
+Link: https://lore.kernel.org/linux-pci/874irqop6b.fsf@draig.linaro.org/
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+[bhelgaas: squash amdgpu BAR selection from
+https: //lore.kernel.org/r/20251114103053.13778-1-ilpo.jarvinen@linux.intel.com]
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Reviewed-by: Christian König <christian.koenig@amd.com>
+Link: https://patch.msgid.link/20251113162628.5946-7-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_device.c  |    4 -
+ drivers/gpu/drm/i915/gt/intel_region_lmem.c |    2 
+ drivers/gpu/drm/xe/xe_vram.c                |    2 
+ drivers/pci/pci-sysfs.c                     |   17 ----
+ drivers/pci/pci.h                           |    4 -
+ drivers/pci/setup-bus.c                     |  100 +++++++++++++++++++++-------
+ drivers/pci/setup-res.c                     |   23 +-----
+ include/linux/pci.h                         |    3 
+ 8 files changed, 93 insertions(+), 62 deletions(-)
+
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+@@ -1736,7 +1736,9 @@ int amdgpu_device_resize_fb_bar(struct a
+       pci_release_resource(adev->pdev, 0);
+-      r = pci_resize_resource(adev->pdev, 0, rbar_size);
++      r = pci_resize_resource(adev->pdev, 0, rbar_size,
++                              (adev->asic_type >= CHIP_BONAIRE) ? 1 << 5
++                                                                : 1 << 2);
+       if (r == -ENOSPC)
+               dev_info(adev->dev,
+                        "Not enough PCI address space for a large BAR.");
+--- a/drivers/gpu/drm/i915/gt/intel_region_lmem.c
++++ b/drivers/gpu/drm/i915/gt/intel_region_lmem.c
+@@ -37,7 +37,7 @@ _resize_bar(struct drm_i915_private *i91
+       _release_bars(pdev);
+-      ret = pci_resize_resource(pdev, resno, bar_size);
++      ret = pci_resize_resource(pdev, resno, bar_size, 0);
+       if (ret) {
+               drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n",
+                        resno, 1 << bar_size, ERR_PTR(ret));
+--- a/drivers/gpu/drm/xe/xe_vram.c
++++ b/drivers/gpu/drm/xe/xe_vram.c
+@@ -56,7 +56,7 @@ static void resize_bar(struct xe_device
+       release_bars(pdev);
+-      ret = pci_resize_resource(pdev, resno, bar_size);
++      ret = pci_resize_resource(pdev, resno, bar_size, 0);
+       if (ret) {
+               drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n",
+                        resno, 1 << bar_size, ERR_PTR(ret));
+--- a/drivers/pci/pci-sysfs.c
++++ b/drivers/pci/pci-sysfs.c
+@@ -1571,18 +1571,13 @@ static ssize_t __resource_resize_store(s
+ {
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct pci_bus *bus = pdev->bus;
+-      struct resource *b_win, *res;
+       unsigned long size;
+-      int ret, i;
++      int ret;
+       u16 cmd;
+       if (kstrtoul(buf, 0, &size) < 0)
+               return -EINVAL;
+-      b_win = pbus_select_window(bus, pci_resource_n(pdev, n));
+-      if (!b_win)
+-              return -EINVAL;
+-
+       device_lock(dev);
+       if (dev->driver || pci_num_vf(pdev)) {
+               ret = -EBUSY;
+@@ -1604,15 +1599,7 @@ static ssize_t __resource_resize_store(s
+       pci_remove_resource_files(pdev);
+-      pci_dev_for_each_resource(pdev, res, i) {
+-              if (i >= PCI_BRIDGE_RESOURCES)
+-                      break;
+-
+-              if (b_win == pbus_select_window(bus, res))
+-                      pci_release_resource(pdev, i);
+-      }
+-
+-      ret = pci_resize_resource(pdev, n, size);
++      ret = pci_resize_resource(pdev, n, size, 0);
+       pci_assign_unassigned_bus_resources(bus);
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -439,8 +439,10 @@ enum pci_bar_type {
+ struct device *pci_get_host_bridge_device(struct pci_dev *dev);
+ void pci_put_host_bridge_device(struct device *dev);
++void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size);
++int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int size,
++                                     int exclude_bars);
+ unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge);
+-int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res);
+ int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
+ int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -2372,18 +2372,16 @@ EXPORT_SYMBOL_GPL(pci_assign_unassigned_
+  * release it when possible. If the bridge window contains assigned
+  * resources, it cannot be released.
+  */
+-int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res)
++static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res,
++                                        struct list_head *saved)
+ {
+       unsigned long type = res->flags;
+       struct pci_dev_resource *dev_res;
+       struct pci_dev *bridge = NULL;
+-      LIST_HEAD(saved);
+       LIST_HEAD(added);
+       LIST_HEAD(failed);
+       unsigned int i;
+-      int ret;
+-
+-      down_read(&pci_bus_sem);
++      int ret = 0;
+       while (!pci_is_root_bus(bus)) {
+               bridge = bus->self;
+@@ -2395,9 +2393,9 @@ int pbus_reassign_bridge_resources(struc
+               /* Ignore BARs which are still in use */
+               if (!res->child) {
+-                      ret = add_to_list(&saved, bridge, res, 0, 0);
++                      ret = add_to_list(saved, bridge, res, 0, 0);
+                       if (ret)
+-                              goto cleanup;
++                              return ret;
+                       pci_release_resource(bridge, i);
+               } else {
+@@ -2420,34 +2418,78 @@ int pbus_reassign_bridge_resources(struc
+               free_list(&added);
+       if (!list_empty(&failed)) {
+-              if (pci_required_resource_failed(&failed, type)) {
++              if (pci_required_resource_failed(&failed, type))
+                       ret = -ENOSPC;
+-                      goto cleanup;
+-              }
+-              /* Only resources with unrelated types failed (again) */
+               free_list(&failed);
++              if (ret)
++                      return ret;
++
++              /* Only resources with unrelated types failed (again) */
+       }
+-      list_for_each_entry(dev_res, &saved, list) {
++      list_for_each_entry(dev_res, saved, list) {
+               struct pci_dev *dev = dev_res->dev;
+               /* Skip the bridge we just assigned resources for */
+               if (bridge == dev)
+                       continue;
++              if (!dev->subordinate)
++                      continue;
++
+               pci_setup_bridge(dev->subordinate);
+       }
+-      free_list(&saved);
+-      up_read(&pci_bus_sem);
+       return 0;
++}
+-cleanup:
+-      /* Restore size and flags */
+-      list_for_each_entry(dev_res, &failed, list)
+-              restore_dev_resource(dev_res);
+-      free_list(&failed);
++int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size,
++                                     int exclude_bars)
++{
++      struct resource *res = pci_resource_n(pdev, resno);
++      struct pci_dev_resource *dev_res;
++      struct pci_bus *bus = pdev->bus;
++      struct resource *b_win, *r;
++      LIST_HEAD(saved);
++      unsigned int i;
++      int ret = 0;
++
++      b_win = pbus_select_window(bus, res);
++      if (!b_win)
++              return -EINVAL;
++
++      pci_dev_for_each_resource(pdev, r, i) {
++              if (i >= PCI_BRIDGE_RESOURCES)
++                      break;
++
++              if (exclude_bars & BIT(i))
++                      continue;
++              if (b_win != pbus_select_window(bus, r))
++                      continue;
++
++              ret = add_to_list(&saved, pdev, r, 0, 0);
++              if (ret)
++                      goto restore;
++              pci_release_resource(pdev, i);
++      }
++
++      pci_resize_resource_set_size(pdev, resno, size);
++
++      if (!bus->self)
++              goto out;
++
++      down_read(&pci_bus_sem);
++      ret = pbus_reassign_bridge_resources(bus, res, &saved);
++      if (ret)
++              goto restore;
++
++out:
++      up_read(&pci_bus_sem);
++      free_list(&saved);
++      return ret;
++
++restore:
+       /* Revert to the old configuration */
+       list_for_each_entry(dev_res, &saved, list) {
+               struct resource *res = dev_res->res;
+@@ -2462,13 +2504,21 @@ cleanup:
+               restore_dev_resource(dev_res);
+-              pci_claim_resource(dev, i);
+-              pci_setup_bridge(dev->subordinate);
+-      }
+-      up_read(&pci_bus_sem);
+-      free_list(&saved);
++              ret = pci_claim_resource(dev, i);
++              if (ret)
++                      continue;
+-      return ret;
++              if (i < PCI_BRIDGE_RESOURCES) {
++                      const char *res_name = pci_resource_name(dev, i);
++
++                      pci_update_resource(dev, i);
++                      pci_info(dev, "%s %pR: old value restored\n",
++                               res_name, res);
++              }
++              if (dev->subordinate)
++                      pci_setup_bridge(dev->subordinate);
++      }
++      goto out;
+ }
+ void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -446,8 +446,7 @@ static bool pci_resize_is_memory_decodin
+       return cmd & PCI_COMMAND_MEMORY;
+ }
+-static void pci_resize_resource_set_size(struct pci_dev *dev, int resno,
+-                                       int size)
++void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size)
+ {
+       resource_size_t res_size = pci_rebar_size_to_bytes(size);
+       struct resource *res = pci_resource_n(dev, resno);
+@@ -458,9 +457,9 @@ static void pci_resize_resource_set_size
+       resource_set_size(res, res_size);
+ }
+-int pci_resize_resource(struct pci_dev *dev, int resno, int size)
++int pci_resize_resource(struct pci_dev *dev, int resno, int size,
++                      int exclude_bars)
+ {
+-      struct resource *res = pci_resource_n(dev, resno);
+       struct pci_host_bridge *host;
+       int old, ret;
+       u32 sizes;
+@@ -470,10 +469,6 @@ int pci_resize_resource(struct pci_dev *
+       if (host->preserve_config)
+               return -ENOTSUPP;
+-      /* Make sure the resource isn't assigned before resizing it. */
+-      if (!(res->flags & IORESOURCE_UNSET))
+-              return -EBUSY;
+-
+       if (pci_resize_is_memory_decoding_enabled(dev, resno))
+               return -EBUSY;
+@@ -492,19 +487,13 @@ int pci_resize_resource(struct pci_dev *
+       if (ret)
+               return ret;
+-      pci_resize_resource_set_size(dev, resno, size);
+-
+-      /* Check if the new config works by trying to assign everything. */
+-      if (dev->bus->self) {
+-              ret = pbus_reassign_bridge_resources(dev->bus, res);
+-              if (ret)
+-                      goto error_resize;
+-      }
++      ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
++      if (ret)
++              goto error_resize;
+       return 0;
+ error_resize:
+       pci_rebar_set_size(dev, resno, old);
+-      pci_resize_resource_set_size(dev, resno, old);
+       return ret;
+ }
+ EXPORT_SYMBOL(pci_resize_resource);
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -1425,7 +1425,8 @@ static inline int pci_rebar_bytes_to_siz
+ }
+ u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar);
+-int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size);
++int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size,
++                                   int exclude_bars);
+ int pci_select_bars(struct pci_dev *dev, unsigned long flags);
+ bool pci_device_is_present(struct pci_dev *pdev);
+ void pci_ignore_hotplug(struct pci_dev *dev);
diff --git a/queue-6.18/pci-free-saved-list-without-holding-pci_bus_sem.patch b/queue-6.18/pci-free-saved-list-without-holding-pci_bus_sem.patch
new file mode 100644 (file)
index 0000000..b4c3d73
--- /dev/null
@@ -0,0 +1,38 @@
+From stable+bounces-274551-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:58 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:32 -0400
+Subject: PCI: Free saved list without holding pci_bus_sem
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Alex Bennée" <alex.bennee@linaro.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714200236.3153778-4-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 1d8a0506f69895b7cfd9d5c4546761c508231a8a ]
+
+Freeing the saved list does not require holding pci_bus_sem, so the
+critical section can be made shorter.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Link: https://patch.msgid.link/20251113162628.5946-6-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/setup-bus.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -2465,8 +2465,8 @@ cleanup:
+               pci_claim_resource(dev, i);
+               pci_setup_bridge(dev->subordinate);
+       }
+-      free_list(&saved);
+       up_read(&pci_bus_sem);
++      free_list(&saved);
+       return ret;
+ }
diff --git a/queue-6.18/pci-imx6-configure-ref_use_pad-before-phy-reset-for-i.mx95.patch b/queue-6.18/pci-imx6-configure-ref_use_pad-before-phy-reset-for-i.mx95.patch
new file mode 100644 (file)
index 0000000..251d5f1
--- /dev/null
@@ -0,0 +1,103 @@
+From stable+bounces-274494-greg=kroah.com@vger.kernel.org Tue Jul 14 19:55:34 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 13:49:21 -0400
+Subject: PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95
+To: stable@vger.kernel.org
+Cc: Richard Zhu <hongxing.zhu@nxp.com>, Manivannan Sadhasivam <mani@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714174921.3041761-2-sashal@kernel.org>
+
+From: Richard Zhu <hongxing.zhu@nxp.com>
+
+[ Upstream commit 0c26b1c34d12d4debfb5363cc0be6cdf68e87ba2 ]
+
+According to the i.MX95 PCIe PHY Databook, the ref_use_pad signal in the
+Common Block Signals section selects the reference clock source connected
+to the PHY pads. Per the specification, any change to this input must be
+followed by a PHY reset assertion to take effect.
+
+Move the REF_USE_PAD configuration before the PHY reset toggle to comply
+with the required initialization sequence.
+
+Fixes: 47f54a902dcd ("PCI: imx6: Toggle the core reset for i.MX95 PCIe")
+Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
+[mani: renamed the callback and helper to match the usecase]
+Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/20260518072715.3166514-2-hongxing.zhu@nxp.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/controller/dwc/pci-imx6.c |   27 ++++++++++++++++++++++++---
+ 1 file changed, 24 insertions(+), 3 deletions(-)
+
+--- a/drivers/pci/controller/dwc/pci-imx6.c
++++ b/drivers/pci/controller/dwc/pci-imx6.c
+@@ -135,6 +135,7 @@ struct imx_pcie_drvdata {
+       const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
+       const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
+       const struct pci_epc_features *epc_features;
++      int (*select_ref_clk_src)(struct imx_pcie *pcie);
+       int (*init_phy)(struct imx_pcie *pcie);
+       int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
+       int (*core_reset)(struct imx_pcie *pcie, bool assert);
+@@ -243,6 +244,24 @@ static unsigned int imx_pcie_grp_offset(
+       return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
+ }
++static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie)
++{
++      bool ext = imx_pcie->enable_ext_refclk;
++
++      /*
++       * Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is
++       * used to select reference clock connected to a pair of pads.
++       *
++       * Any change in this input must be followed by phy_reset assertion.
++       */
++
++      regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
++                         IMX95_PCIE_REF_USE_PAD,
++                         ext ? IMX95_PCIE_REF_USE_PAD : 0);
++
++      return 0;
++}
++
+ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
+ {
+       bool ext = imx_pcie->enable_ext_refclk;
+@@ -265,9 +284,6 @@ static int imx95_pcie_init_phy(struct im
+                       IMX95_PCIE_PHY_CR_PARA_SEL,
+                       IMX95_PCIE_PHY_CR_PARA_SEL);
+-      regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
+-                         IMX95_PCIE_REF_USE_PAD,
+-                         ext ? IMX95_PCIE_REF_USE_PAD : 0);
+       regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
+                          IMX95_PCIE_REF_CLKEN,
+                          ext ? 0 : IMX95_PCIE_REF_CLKEN);
+@@ -1232,6 +1248,9 @@ static int imx_pcie_host_init(struct dw_
+               pp->bridge->disable_device = imx_pcie_disable_device;
+       }
++      if (imx_pcie->drvdata->select_ref_clk_src)
++              imx_pcie->drvdata->select_ref_clk_src(imx_pcie);
++
+       imx_pcie_assert_core_reset(imx_pcie);
+       if (imx_pcie->drvdata->init_phy)
+@@ -1932,6 +1951,7 @@ static const struct imx_pcie_drvdata drv
+               .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
+               .core_reset = imx95_pcie_core_reset,
+               .init_phy = imx95_pcie_init_phy,
++              .select_ref_clk_src = imx95_pcie_select_ref_clk_src,
+               .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
+               .enable_ref_clk = imx95_pcie_enable_ref_clk,
+       },
+@@ -1986,6 +2006,7 @@ static const struct imx_pcie_drvdata drv
+               .ltssm_mask = IMX95_PCIE_LTSSM_EN,
+               .mode_off[0]  = IMX95_PE0_GEN_CTRL_1,
+               .mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
++              .select_ref_clk_src = imx95_pcie_select_ref_clk_src,
+               .init_phy = imx95_pcie_init_phy,
+               .core_reset = imx95_pcie_core_reset,
+               .wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
diff --git a/queue-6.18/pci-imx6-fix-reference-clock-source-selection-for-i.mx95.patch b/queue-6.18/pci-imx6-fix-reference-clock-source-selection-for-i.mx95.patch
new file mode 100644 (file)
index 0000000..46ec561
--- /dev/null
@@ -0,0 +1,71 @@
+From stable+bounces-274491-greg=kroah.com@vger.kernel.org Tue Jul 14 19:55:28 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 13:49:20 -0400
+Subject: PCI: imx6: Fix reference clock source selection for i.MX95
+To: stable@vger.kernel.org
+Cc: Franz Schnyder <franz.schnyder@toradex.com>, Manivannan Sadhasivam <mani@kernel.org>, Bjorn Helgaas <bhelgaas@google.com>, Richard Zhu <hongxing.zhu@nxp.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714174921.3041761-1-sashal@kernel.org>
+
+From: Franz Schnyder <franz.schnyder@toradex.com>
+
+[ Upstream commit 88cc4cbe08bba27bb58888d25d336774aa0ccab1 ]
+
+In the PCIe PHY init for the i.MX95, the reference clock source selection
+uses a conditional instead of always passing the mask. This currently
+breaks functionality if the internal refclk is used.
+
+To fix this issue, always pass IMX95_PCIE_REF_USE_PAD as the mask and clear
+bit if external refclk is not used. This essentially swaps the parameters.
+
+Fixes: d8574ce57d76 ("PCI: imx6: Add external reference clock input mode support")
+Signed-off-by: Franz Schnyder <franz.schnyder@toradex.com>
+Signed-off-by: Manivannan Sadhasivam <mani@kernel.org>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Acked-by: Richard Zhu <hongxing.zhu@nxp.com>
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/20260325093118.684142-1-fra.schnyder@gmail.com
+Stable-dep-of: 0c26b1c34d12 ("PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/controller/dwc/pci-imx6.c |   14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/pci/controller/dwc/pci-imx6.c
++++ b/drivers/pci/controller/dwc/pci-imx6.c
+@@ -150,6 +150,7 @@ struct imx_lut_data {
+ struct imx_pcie {
+       struct dw_pcie          *pci;
+       struct gpio_desc        *reset_gpiod;
++      bool                    enable_ext_refclk;
+       struct clk_bulk_data    *clks;
+       int                     num_clks;
+       struct regmap           *iomuxc_gpr;
+@@ -244,6 +245,8 @@ static unsigned int imx_pcie_grp_offset(
+ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
+ {
++      bool ext = imx_pcie->enable_ext_refclk;
++
+       /*
+        * ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
+        * Through Beacon or PERST# De-assertion
+@@ -262,13 +265,12 @@ static int imx95_pcie_init_phy(struct im
+                       IMX95_PCIE_PHY_CR_PARA_SEL,
+                       IMX95_PCIE_PHY_CR_PARA_SEL);
+-      regmap_update_bits(imx_pcie->iomuxc_gpr,
+-                         IMX95_PCIE_PHY_GEN_CTRL,
+-                         IMX95_PCIE_REF_USE_PAD, 0);
+-      regmap_update_bits(imx_pcie->iomuxc_gpr,
+-                         IMX95_PCIE_SS_RW_REG_0,
++      regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
++                         IMX95_PCIE_REF_USE_PAD,
++                         ext ? IMX95_PCIE_REF_USE_PAD : 0);
++      regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
+                          IMX95_PCIE_REF_CLKEN,
+-                         IMX95_PCIE_REF_CLKEN);
++                         ext ? 0 : IMX95_PCIE_REF_CLKEN);
+       return 0;
+ }
diff --git a/queue-6.18/pci-iov-adjust-barsz-when-changing-bar-size.patch b/queue-6.18/pci-iov-adjust-barsz-when-changing-bar-size.patch
new file mode 100644 (file)
index 0000000..0199956
--- /dev/null
@@ -0,0 +1,141 @@
+From stable+bounces-274545-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:46 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:29 -0400
+Subject: PCI/IOV: Adjust ->barsz[] when changing BAR size
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Alex Bennée" <alex.bennee@linaro.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714200236.3153778-1-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 4687b3315a3f76647746f5b7f92684cf1045b085 ]
+
+pci_rebar_set_size() adjusts BAR size for both normal and IOV BARs. The
+struct pci_sriov keeps a cached copy of BAR size in ->barsz[] which is not
+adjusted by pci_rebar_set_size() but by pci_iov_resource_set_size().
+pci_iov_resource_set_size() is called also from
+pci_resize_resource_set_size().
+
+The current arrangement is problematic once BAR resize algorithm starts to
+roll back changes properly in case of a failure. The normal resource
+fitting algorithm rolls back resource size using the struct
+pci_dev_resource easily but also calling pci_resize_resource_set_size() or
+pci_iov_resource_set_size() to roll back BAR size would be an extra burden,
+whereas combining ->barsz[] update with pci_rebar_set_size() naturally
+rolls back it when restoring the old BAR size on a different layer of the
+BAR resize operation.
+
+Thus, rework pci_rebar_set_size() to also update ->barsz[].
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Link: https://patch.msgid.link/20251113162628.5946-3-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/iov.c       |   15 ++++-----------
+ drivers/pci/pci.c       |    4 ++++
+ drivers/pci/pci.h       |    5 ++---
+ drivers/pci/setup-res.c |   10 ++++------
+ 4 files changed, 14 insertions(+), 20 deletions(-)
+
+--- a/drivers/pci/iov.c
++++ b/drivers/pci/iov.c
+@@ -158,8 +158,7 @@ resource_size_t pci_iov_resource_size(st
+       return dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)];
+ }
+-void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
+-                             resource_size_t size)
++void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size)
+ {
+       if (!pci_resource_is_iov(resno)) {
+               pci_warn(dev, "%s is not an IOV resource\n",
+@@ -167,7 +166,8 @@ void pci_iov_resource_set_size(struct pc
+               return;
+       }
+-      dev->sriov->barsz[pci_resource_num_to_vf_bar(resno)] = size;
++      resno = pci_resource_num_to_vf_bar(resno);
++      dev->sriov->barsz[resno] = pci_rebar_size_to_bytes(size);
+ }
+ bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
+@@ -1345,7 +1345,6 @@ EXPORT_SYMBOL_GPL(pci_sriov_configure_si
+ int pci_iov_vf_bar_set_size(struct pci_dev *dev, int resno, int size)
+ {
+       u32 sizes;
+-      int ret;
+       if (!pci_resource_is_iov(resno))
+               return -EINVAL;
+@@ -1360,13 +1359,7 @@ int pci_iov_vf_bar_set_size(struct pci_d
+       if (!(sizes & BIT(size)))
+               return -EINVAL;
+-      ret = pci_rebar_set_size(dev, resno, size);
+-      if (ret)
+-              return ret;
+-
+-      pci_iov_resource_set_size(dev, resno, pci_rebar_size_to_bytes(size));
+-
+-      return 0;
++      return pci_rebar_set_size(dev, resno, size);
+ }
+ EXPORT_SYMBOL_GPL(pci_iov_vf_bar_set_size);
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -3797,6 +3797,10 @@ int pci_rebar_set_size(struct pci_dev *p
+       ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+       ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
+       pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
++
++      if (pci_resource_is_iov(bar))
++              pci_iov_resource_set_size(pdev, bar, size);
++
+       return 0;
+ }
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -826,8 +826,7 @@ void pci_iov_update_resource(struct pci_
+ resource_size_t pci_sriov_resource_alignment(struct pci_dev *dev, int resno);
+ void pci_restore_iov_state(struct pci_dev *dev);
+ int pci_iov_bus_range(struct pci_bus *bus);
+-void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
+-                             resource_size_t size);
++void pci_iov_resource_set_size(struct pci_dev *dev, int resno, int size);
+ bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev);
+ static inline u16 pci_iov_vf_rebar_cap(struct pci_dev *dev)
+ {
+@@ -869,7 +868,7 @@ static inline int pci_iov_bus_range(stru
+       return 0;
+ }
+ static inline void pci_iov_resource_set_size(struct pci_dev *dev, int resno,
+-                                           resource_size_t size) { }
++                                           int size) { }
+ static inline bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
+ {
+       return false;
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -452,12 +452,10 @@ static void pci_resize_resource_set_size
+       resource_size_t res_size = pci_rebar_size_to_bytes(size);
+       struct resource *res = pci_resource_n(dev, resno);
+-      if (!pci_resource_is_iov(resno)) {
+-              resource_set_size(res, res_size);
+-      } else {
+-              resource_set_size(res, res_size * pci_sriov_get_totalvfs(dev));
+-              pci_iov_resource_set_size(dev, resno, res_size);
+-      }
++      if (pci_resource_is_iov(resno))
++              res_size *= pci_sriov_get_totalvfs(dev);
++
++      resource_set_size(res, res_size);
+ }
+ int pci_resize_resource(struct pci_dev *dev, int resno, int size)
diff --git a/queue-6.18/pci-move-resizable-bar-code-to-rebar.c.patch b/queue-6.18/pci-move-resizable-bar-code-to-rebar.c.patch
new file mode 100644 (file)
index 0000000..277975a
--- /dev/null
@@ -0,0 +1,579 @@
+From stable+bounces-274553-greg=kroah.com@vger.kernel.org Tue Jul 14 22:04:33 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:35 -0400
+Subject: PCI: Move Resizable BAR code to rebar.c
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Christian König" <christian.koenig@amd.com>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714200236.3153778-7-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 9f71938cd77f32a448f40a288e409eca60e55486 ]
+
+For lack of a better place to put it, Resizable BAR code has been placed
+inside pci.c and setup-res.c that do not use it for anything.  Upcoming
+changes are going to add more Resizable BAR related functions, increasing
+the code size.
+
+As pci.c is huge as is, move the Resizable BAR related code and the BAR
+resize code from setup-res.c to rebar.c.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Christian König <christian.koenig@amd.com>
+Link: https://patch.msgid.link/20251113180053.27944-2-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/driver-api/pci/pci.rst |    3 
+ drivers/pci/Makefile                 |    2 
+ drivers/pci/pci.c                    |  149 ---------------------
+ drivers/pci/pci.h                    |    1 
+ drivers/pci/rebar.c                  |  247 +++++++++++++++++++++++++++++++++++
+ drivers/pci/setup-res.c              |   85 ------------
+ 6 files changed, 252 insertions(+), 235 deletions(-)
+ create mode 100644 drivers/pci/rebar.c
+
+--- a/Documentation/driver-api/pci/pci.rst
++++ b/Documentation/driver-api/pci/pci.rst
+@@ -37,6 +37,9 @@ PCI Support Library
+ .. kernel-doc:: drivers/pci/slot.c
+    :export:
++.. kernel-doc:: drivers/pci/rebar.c
++   :export:
++
+ .. kernel-doc:: drivers/pci/rom.c
+    :export:
+--- a/drivers/pci/Makefile
++++ b/drivers/pci/Makefile
+@@ -4,7 +4,7 @@
+ obj-$(CONFIG_PCI)             += access.o bus.o probe.o host-bridge.o \
+                                  remove.o pci.o pci-driver.o search.o \
+-                                 rom.o setup-res.o irq.o vpd.o \
++                                 rebar.o rom.o setup-res.o irq.o vpd.o \
+                                  setup-bus.o vc.o mmap.o devres.o
+ obj-$(CONFIG_PCI)             += msi/
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -1823,32 +1823,6 @@ static void pci_restore_config_space(str
+       }
+ }
+-static void pci_restore_rebar_state(struct pci_dev *pdev)
+-{
+-      unsigned int pos, nbars, i;
+-      u32 ctrl;
+-
+-      pos = pdev->rebar_cap;
+-      if (!pos)
+-              return;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-      nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+-
+-      for (i = 0; i < nbars; i++, pos += 8) {
+-              struct resource *res;
+-              int bar_idx, size;
+-
+-              pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-              bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+-              res = pci_resource_n(pdev, bar_idx);
+-              size = pci_rebar_bytes_to_size(resource_size(res));
+-              ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+-              ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
+-              pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+-      }
+-}
+-
+ /**
+  * pci_restore_state - Restore the saved state of a PCI device
+  * @dev: PCI device that we're dealing with
+@@ -3681,129 +3655,6 @@ void pci_acs_init(struct pci_dev *dev)
+       dev->acs_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+ }
+-void pci_rebar_init(struct pci_dev *pdev)
+-{
+-      pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
+-}
+-
+-/**
+- * pci_rebar_find_pos - find position of resize ctrl reg for BAR
+- * @pdev: PCI device
+- * @bar: BAR to find
+- *
+- * Helper to find the position of the ctrl register for a BAR.
+- * Returns -ENOTSUPP if resizable BARs are not supported at all.
+- * Returns -ENOENT if no ctrl register for the BAR could be found.
+- */
+-static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
+-{
+-      unsigned int pos, nbars, i;
+-      u32 ctrl;
+-
+-      if (pci_resource_is_iov(bar)) {
+-              pos = pci_iov_vf_rebar_cap(pdev);
+-              bar = pci_resource_num_to_vf_bar(bar);
+-      } else {
+-              pos = pdev->rebar_cap;
+-      }
+-
+-      if (!pos)
+-              return -ENOTSUPP;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-      nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+-
+-      for (i = 0; i < nbars; i++, pos += 8) {
+-              int bar_idx;
+-
+-              pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-              bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl);
+-              if (bar_idx == bar)
+-                      return pos;
+-      }
+-
+-      return -ENOENT;
+-}
+-
+-/**
+- * pci_rebar_get_possible_sizes - get possible sizes for BAR
+- * @pdev: PCI device
+- * @bar: BAR to query
+- *
+- * Get the possible sizes of a resizable BAR as bitmask defined in the spec
+- * (bit 0=1MB, bit 31=128TB). Returns 0 if BAR isn't resizable.
+- */
+-u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
+-{
+-      int pos;
+-      u32 cap;
+-
+-      pos = pci_rebar_find_pos(pdev, bar);
+-      if (pos < 0)
+-              return 0;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
+-      cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
+-
+-      /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
+-      if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
+-          bar == 0 && cap == 0x700)
+-              return 0x3f00;
+-
+-      return cap;
+-}
+-EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
+-
+-/**
+- * pci_rebar_get_current_size - get the current size of a BAR
+- * @pdev: PCI device
+- * @bar: BAR to set size to
+- *
+- * Read the size of a BAR from the resizable BAR config.
+- * Returns size if found or negative error code.
+- */
+-int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
+-{
+-      int pos;
+-      u32 ctrl;
+-
+-      pos = pci_rebar_find_pos(pdev, bar);
+-      if (pos < 0)
+-              return pos;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-      return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl);
+-}
+-
+-/**
+- * pci_rebar_set_size - set a new size for a BAR
+- * @pdev: PCI device
+- * @bar: BAR to set size to
+- * @size: new size as defined in the spec (0=1MB, 31=128TB)
+- *
+- * Set the new size of a BAR as defined in the spec.
+- * Returns zero if resizing was successful, error code otherwise.
+- */
+-int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
+-{
+-      int pos;
+-      u32 ctrl;
+-
+-      pos = pci_rebar_find_pos(pdev, bar);
+-      if (pos < 0)
+-              return pos;
+-
+-      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
+-      ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
+-      ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
+-      pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
+-
+-      if (pci_resource_is_iov(bar))
+-              pci_iov_resource_set_size(pdev, bar, size);
+-
+-      return 0;
+-}
+-
+ /**
+  * pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port
+  * @dev: the PCI device
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -1042,6 +1042,7 @@ static inline int acpi_get_rc_resources(
+ #endif
+ void pci_rebar_init(struct pci_dev *pdev);
++void pci_restore_rebar_state(struct pci_dev *pdev);
+ int pci_rebar_get_current_size(struct pci_dev *pdev, int bar);
+ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size);
+ static inline u64 pci_rebar_size_to_bytes(int size)
+--- /dev/null
++++ b/drivers/pci/rebar.c
+@@ -0,0 +1,247 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PCI Resizable BAR Extended Capability handling.
++ */
++
++#include <linux/bitfield.h>
++#include <linux/errno.h>
++#include <linux/export.h>
++#include <linux/ioport.h>
++#include <linux/pci.h>
++#include <linux/types.h>
++
++#include "pci.h"
++
++void pci_rebar_init(struct pci_dev *pdev)
++{
++      pdev->rebar_cap = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR);
++}
++
++/**
++ * pci_rebar_find_pos - find position of resize ctrl reg for BAR
++ * @pdev: PCI device
++ * @bar: BAR to find
++ *
++ * Helper to find the position of the ctrl register for a BAR.
++ * Returns -ENOTSUPP if resizable BARs are not supported at all.
++ * Returns -ENOENT if no ctrl register for the BAR could be found.
++ */
++static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
++{
++      unsigned int pos, nbars, i;
++      u32 ctrl;
++
++      if (pci_resource_is_iov(bar)) {
++              pos = pci_iov_vf_rebar_cap(pdev);
++              bar = pci_resource_num_to_vf_bar(bar);
++      } else {
++              pos = pdev->rebar_cap;
++      }
++
++      if (!pos)
++              return -ENOTSUPP;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
++
++      for (i = 0; i < nbars; i++, pos += 8) {
++              int bar_idx;
++
++              pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++              bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl);
++              if (bar_idx == bar)
++                      return pos;
++      }
++
++      return -ENOENT;
++}
++
++/**
++ * pci_rebar_get_possible_sizes - get possible sizes for BAR
++ * @pdev: PCI device
++ * @bar: BAR to query
++ *
++ * Get the possible sizes of a resizable BAR as bitmask defined in the spec
++ * (bit 0=1MB, bit 31=128TB). Returns 0 if BAR isn't resizable.
++ */
++u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
++{
++      int pos;
++      u32 cap;
++
++      pos = pci_rebar_find_pos(pdev, bar);
++      if (pos < 0)
++              return 0;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
++      cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
++
++      /* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
++      if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
++          bar == 0 && cap == 0x700)
++              return 0x3f00;
++
++      return cap;
++}
++EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
++
++/**
++ * pci_rebar_get_current_size - get the current size of a BAR
++ * @pdev: PCI device
++ * @bar: BAR to set size to
++ *
++ * Read the size of a BAR from the resizable BAR config.
++ * Returns size if found or negative error code.
++ */
++int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
++{
++      int pos;
++      u32 ctrl;
++
++      pos = pci_rebar_find_pos(pdev, bar);
++      if (pos < 0)
++              return pos;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl);
++}
++
++/**
++ * pci_rebar_set_size - set a new size for a BAR
++ * @pdev: PCI device
++ * @bar: BAR to set size to
++ * @size: new size as defined in the spec (0=1MB, 31=128TB)
++ *
++ * Set the new size of a BAR as defined in the spec.
++ * Returns zero if resizing was successful, error code otherwise.
++ */
++int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
++{
++      int pos;
++      u32 ctrl;
++
++      pos = pci_rebar_find_pos(pdev, bar);
++      if (pos < 0)
++              return pos;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
++      ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
++      pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
++
++      if (pci_resource_is_iov(bar))
++              pci_iov_resource_set_size(pdev, bar, size);
++
++      return 0;
++}
++
++void pci_restore_rebar_state(struct pci_dev *pdev)
++{
++      unsigned int pos, nbars, i;
++      u32 ctrl;
++
++      pos = pdev->rebar_cap;
++      if (!pos)
++              return;
++
++      pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
++
++      for (i = 0; i < nbars; i++, pos += 8) {
++              struct resource *res;
++              int bar_idx, size;
++
++              pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++              bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
++              res = pci_resource_n(pdev, bar_idx);
++              size = pci_rebar_bytes_to_size(resource_size(res));
++              ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
++              ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
++              pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
++      }
++}
++
++static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev,
++                                                int resno)
++{
++      u16 cmd;
++
++      if (pci_resource_is_iov(resno))
++              return pci_iov_is_memory_decoding_enabled(dev);
++
++      pci_read_config_word(dev, PCI_COMMAND, &cmd);
++
++      return cmd & PCI_COMMAND_MEMORY;
++}
++
++void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size)
++{
++      resource_size_t res_size = pci_rebar_size_to_bytes(size);
++      struct resource *res = pci_resource_n(dev, resno);
++
++      if (pci_resource_is_iov(resno))
++              res_size *= pci_sriov_get_totalvfs(dev);
++
++      resource_set_size(res, res_size);
++}
++
++/**
++ * pci_resize_resource - reconfigure a Resizable BAR and resources
++ * @dev: the PCI device
++ * @resno: index of the BAR to be resized
++ * @size: new size as defined in the spec (0=1MB, 31=128TB)
++ * @exclude_bars: a mask of BARs that should not be released
++ *
++ * Reconfigure @resno to @size and re-run resource assignment algorithm
++ * with the new size.
++ *
++ * Prior to resize, release @dev resources that share a bridge window with
++ * @resno.  This unpins the bridge window resource to allow changing it.
++ *
++ * The caller may prevent releasing a particular BAR by providing
++ * @exclude_bars mask, but this may result in the resize operation failing
++ * due to insufficient space.
++ *
++ * Return: 0 on success, or negative on error. In case of an error, the
++ *         resources are restored to their original places.
++ */
++int pci_resize_resource(struct pci_dev *dev, int resno, int size,
++                      int exclude_bars)
++{
++      struct pci_host_bridge *host;
++      int old, ret;
++      u32 sizes;
++
++      /* Check if we must preserve the firmware's resource assignment */
++      host = pci_find_host_bridge(dev->bus);
++      if (host->preserve_config)
++              return -ENOTSUPP;
++
++      if (pci_resize_is_memory_decoding_enabled(dev, resno))
++              return -EBUSY;
++
++      sizes = pci_rebar_get_possible_sizes(dev, resno);
++      if (!sizes)
++              return -ENOTSUPP;
++
++      if (!(sizes & BIT(size)))
++              return -EINVAL;
++
++      old = pci_rebar_get_current_size(dev, resno);
++      if (old < 0)
++              return old;
++
++      ret = pci_rebar_set_size(dev, resno, size);
++      if (ret)
++              return ret;
++
++      ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
++      if (ret)
++              goto error_resize;
++      return 0;
++
++error_resize:
++      pci_rebar_set_size(dev, resno, old);
++      return ret;
++}
++EXPORT_SYMBOL(pci_resize_resource);
+--- a/drivers/pci/setup-res.c
++++ b/drivers/pci/setup-res.c
+@@ -433,91 +433,6 @@ int pci_release_resource(struct pci_dev
+ }
+ EXPORT_SYMBOL(pci_release_resource);
+-static bool pci_resize_is_memory_decoding_enabled(struct pci_dev *dev,
+-                                                int resno)
+-{
+-      u16 cmd;
+-
+-      if (pci_resource_is_iov(resno))
+-              return pci_iov_is_memory_decoding_enabled(dev);
+-
+-      pci_read_config_word(dev, PCI_COMMAND, &cmd);
+-
+-      return cmd & PCI_COMMAND_MEMORY;
+-}
+-
+-void pci_resize_resource_set_size(struct pci_dev *dev, int resno, int size)
+-{
+-      resource_size_t res_size = pci_rebar_size_to_bytes(size);
+-      struct resource *res = pci_resource_n(dev, resno);
+-
+-      if (pci_resource_is_iov(resno))
+-              res_size *= pci_sriov_get_totalvfs(dev);
+-
+-      resource_set_size(res, res_size);
+-}
+-
+-/**
+- * pci_resize_resource - reconfigure a Resizable BAR and resources
+- * @dev: the PCI device
+- * @resno: index of the BAR to be resized
+- * @size: new size as defined in the spec (0=1MB, 31=128TB)
+- * @exclude_bars: a mask of BARs that should not be released
+- *
+- * Reconfigure @resno to @size and re-run resource assignment algorithm
+- * with the new size.
+- *
+- * Prior to resize, release @dev resources that share a bridge window with
+- * @resno.  This unpins the bridge window resource to allow changing it.
+- *
+- * The caller may prevent releasing a particular BAR by providing
+- * @exclude_bars mask, but this may result in the resize operation failing
+- * due to insufficient space.
+- *
+- * Return: 0 on success, or negative on error. In case of an error, the
+- *         resources are restored to their original places.
+- */
+-int pci_resize_resource(struct pci_dev *dev, int resno, int size,
+-                      int exclude_bars)
+-{
+-      struct pci_host_bridge *host;
+-      int old, ret;
+-      u32 sizes;
+-
+-      /* Check if we must preserve the firmware's resource assignment */
+-      host = pci_find_host_bridge(dev->bus);
+-      if (host->preserve_config)
+-              return -ENOTSUPP;
+-
+-      if (pci_resize_is_memory_decoding_enabled(dev, resno))
+-              return -EBUSY;
+-
+-      sizes = pci_rebar_get_possible_sizes(dev, resno);
+-      if (!sizes)
+-              return -ENOTSUPP;
+-
+-      if (!(sizes & BIT(size)))
+-              return -EINVAL;
+-
+-      old = pci_rebar_get_current_size(dev, resno);
+-      if (old < 0)
+-              return old;
+-
+-      ret = pci_rebar_set_size(dev, resno, size);
+-      if (ret)
+-              return ret;
+-
+-      ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars);
+-      if (ret)
+-              goto error_resize;
+-      return 0;
+-
+-error_resize:
+-      pci_rebar_set_size(dev, resno, old);
+-      return ret;
+-}
+-EXPORT_SYMBOL(pci_resize_resource);
+-
+ int pci_enable_resources(struct pci_dev *dev, int mask)
+ {
+       u16 cmd, old_cmd;
diff --git a/queue-6.18/pci-skip-resizable-bar-restore-on-read-error.patch b/queue-6.18/pci-skip-resizable-bar-restore-on-read-error.patch
new file mode 100644 (file)
index 0000000..db0402e
--- /dev/null
@@ -0,0 +1,61 @@
+From stable+bounces-274548-greg=kroah.com@vger.kernel.org Tue Jul 14 22:04:11 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:36 -0400
+Subject: PCI: Skip Resizable BAR restore on read error
+To: stable@vger.kernel.org
+Cc: Marco Nenciarini <mnencia@kcore.it>, Bjorn Helgaas <bhelgaas@google.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714200236.3153778-8-sashal@kernel.org>
+
+From: Marco Nenciarini <mnencia@kcore.it>
+
+[ Upstream commit ee7471fe968d210939be9046089a924cd23c8c3b ]
+
+pci_restore_rebar_state() uses the Resizable BAR Control register to decide
+how many BARs to restore (nbars) and which BAR each iteration addresses
+(bar_idx).
+
+When a device does not respond, config reads typically return
+PCI_ERROR_RESPONSE (~0).  Both fields are 3 bits wide, so nbars and bar_idx
+both evaluate to 7, past the spec's valid ranges for both fields.
+pci_resource_n() then returns an unrelated resource slot, whose size is
+used to derive a nonsensical value written back to the Resizable BAR
+Control register.
+
+Bail out if any Resizable BAR Control read returns PCI_ERROR_RESPONSE. No
+further BARs are touched, which is safe because a config read that returns
+PCI_ERROR_RESPONSE indicates the device is unreachable and restoration is
+pointless.
+
+Fixes: d3252ace0bc6 ("PCI: Restore resized BAR state on resume")
+Signed-off-by: Marco Nenciarini <mnencia@kcore.it>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Cc: stable@vger.kernel.org
+Link: https://patch.msgid.link/666cac19b5daa0ab0e0ab64454e76b4d24465dbd.1776429882.git.mnencia@kcore.it
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/rebar.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/pci/rebar.c
++++ b/drivers/pci/rebar.c
+@@ -145,6 +145,9 @@ void pci_restore_rebar_state(struct pci_
+               return;
+       pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++      if (PCI_POSSIBLE_ERROR(ctrl))
++              return;
++
+       nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
+       for (i = 0; i < nbars; i++, pos += 8) {
+@@ -152,6 +155,9 @@ void pci_restore_rebar_state(struct pci_
+               int bar_idx, size;
+               pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
++              if (PCI_POSSIBLE_ERROR(ctrl))
++                      return;
++
+               bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
+               res = pci_resource_n(pdev, bar_idx);
+               size = pci_rebar_bytes_to_size(resource_size(res));
diff --git a/queue-6.18/pci-try-bar-resize-even-when-no-window-was-released.patch b/queue-6.18/pci-try-bar-resize-even-when-no-window-was-released.patch
new file mode 100644 (file)
index 0000000..08f53ca
--- /dev/null
@@ -0,0 +1,55 @@
+From stable+bounces-274546-greg=kroah.com@vger.kernel.org Tue Jul 14 22:02:57 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 16:02:31 -0400
+Subject: PCI: Try BAR resize even when no window was released
+To: stable@vger.kernel.org
+Cc: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>, "Bjorn Helgaas" <bhelgaas@google.com>, "Alex Bennée" <alex.bennee@linaro.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260714200236.3153778-3-sashal@kernel.org>
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 121d3e9e4b217f2f23715901fb15c6a3ca2bab46 ]
+
+Usually, resizing BARs requires releasing bridge windows in order to
+resize it to fit a larger BAR into the window. This is not always the
+case, however, FW could have made the window large enough to accommodate
+larger BAR as is, or the user might prefer to shrink a BAR to make more
+space for another Resizable BAR.
+
+Thus, replace the check that requires that at least one bridge window
+was released with a check that simply ensures bridge is not NULL.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Tested-by: Alex Bennée <alex.bennee@linaro.org> # AVA, AMD GPU
+Link: https://patch.msgid.link/20251113162628.5946-5-ilpo.jarvinen@linux.intel.com
+Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/pci/setup-bus.c |    6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/drivers/pci/setup-bus.c
++++ b/drivers/pci/setup-bus.c
+@@ -2376,7 +2376,7 @@ int pbus_reassign_bridge_resources(struc
+ {
+       unsigned long type = res->flags;
+       struct pci_dev_resource *dev_res;
+-      struct pci_dev *bridge;
++      struct pci_dev *bridge = NULL;
+       LIST_HEAD(saved);
+       LIST_HEAD(added);
+       LIST_HEAD(failed);
+@@ -2411,10 +2411,8 @@ int pbus_reassign_bridge_resources(struc
+               bus = bus->parent;
+       }
+-      if (list_empty(&saved)) {
+-              up_read(&pci_bus_sem);
++      if (!bridge)
+               return -ENOENT;
+-      }
+       __pci_bus_size_bridges(bridge->subordinate, &added);
+       __pci_bridge_assign_resources(bridge, &added, &failed);
diff --git a/queue-6.18/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch b/queue-6.18/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch
new file mode 100644 (file)
index 0000000..8913df7
--- /dev/null
@@ -0,0 +1,274 @@
+From stable+bounces-276776-greg=kroah.com@vger.kernel.org Thu Jul 16 18:47:46 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 12:45:14 -0400
+Subject: proc: protect ptrace_may_access() with exec_update_lock (FD links)
+To: stable@vger.kernel.org
+Cc: Jann Horn <jannh@google.com>, "Christian Brauner (Amutable)" <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716164514.668766-2-sashal@kernel.org>
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 6255da28d4bb5349fe18e84cb043ccd394eba75d ]
+
+proc_pid_get_link() and proc_pid_readlink() currently look up the task from
+the pid once, then do the ptrace access check on that task, then look up
+the task from the pid a second time to do the actual access.
+That's racy in several ways.
+
+To fix it, pass the task to the ->proc_get_link() handler, and instead of
+proc_fd_access_allowed(), introduce a new helper call_proc_get_link() that
+looks up and locks the task, does the access check, and calls
+->proc_get_link().
+
+Fixes: 778c1144771f ("[PATCH] proc: Use sane permission checks on the /proc/<pid>/fd/ symlinks")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-2-5c3d20e0ac33@google.com
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/base.c     |  119 ++++++++++++++++++++---------------------------------
+ fs/proc/fd.c       |   25 ++++-------
+ fs/proc/internal.h |    2 
+ 3 files changed, 58 insertions(+), 88 deletions(-)
+
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -218,33 +218,24 @@ static int get_task_root(struct task_str
+       return result;
+ }
+-static int proc_cwd_link(struct dentry *dentry, struct path *path)
++static int proc_cwd_link(struct dentry *dentry, struct path *path,
++                       struct task_struct *task)
+ {
+-      struct task_struct *task = get_proc_task(d_inode(dentry));
+       int result = -ENOENT;
+-      if (task) {
+-              task_lock(task);
+-              if (task->fs) {
+-                      get_fs_pwd(task->fs, path);
+-                      result = 0;
+-              }
+-              task_unlock(task);
+-              put_task_struct(task);
++      task_lock(task);
++      if (task->fs) {
++              get_fs_pwd(task->fs, path);
++              result = 0;
+       }
++      task_unlock(task);
+       return result;
+ }
+-static int proc_root_link(struct dentry *dentry, struct path *path)
++static int proc_root_link(struct dentry *dentry, struct path *path,
++                        struct task_struct *task)
+ {
+-      struct task_struct *task = get_proc_task(d_inode(dentry));
+-      int result = -ENOENT;
+-
+-      if (task) {
+-              result = get_task_root(task, path);
+-              put_task_struct(task);
+-      }
+-      return result;
++      return get_task_root(task, path);
+ }
+ /*
+@@ -704,23 +695,6 @@ static int proc_pid_syscall(struct seq_f
+ /*                       Here the fs part begins                        */
+ /************************************************************************/
+-/* permission checks */
+-static bool proc_fd_access_allowed(struct inode *inode)
+-{
+-      struct task_struct *task;
+-      bool allowed = false;
+-      /* Allow access to a task's file descriptors if it is us or we
+-       * may use ptrace attach to the process and find out that
+-       * information.
+-       */
+-      task = get_proc_task(inode);
+-      if (task) {
+-              allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
+-              put_task_struct(task);
+-      }
+-      return allowed;
+-}
+-
+ int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+                struct iattr *attr)
+ {
+@@ -1777,16 +1751,12 @@ static const struct file_operations proc
+       .release        = single_release,
+ };
+-static int proc_exe_link(struct dentry *dentry, struct path *exe_path)
++static int proc_exe_link(struct dentry *dentry, struct path *exe_path,
++                       struct task_struct *task)
+ {
+-      struct task_struct *task;
+       struct file *exe_file;
+-      task = get_proc_task(d_inode(dentry));
+-      if (!task)
+-              return -ENOENT;
+       exe_file = get_task_exe_file(task);
+-      put_task_struct(task);
+       if (exe_file) {
+               *exe_path = exe_file->f_path;
+               path_get(&exe_file->f_path);
+@@ -1796,26 +1766,42 @@ static int proc_exe_link(struct dentry *
+               return -ENOENT;
+ }
++static int call_proc_get_link(struct dentry *dentry, struct inode *inode, struct path *path_out)
++{
++      struct task_struct *task;
++      int ret;
++
++      task = get_proc_task(inode);
++      if (!task)
++              return -ENOENT;
++      ret = down_read_killable(&task->signal->exec_update_lock);
++      if (ret)
++              goto out_put_task;
++      if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
++              ret = -EACCES;
++              goto out;
++      }
++      ret = PROC_I(inode)->op.proc_get_link(dentry, path_out, task);
++
++out:
++      up_read(&task->signal->exec_update_lock);
++out_put_task:
++      put_task_struct(task);
++      return ret;
++}
++
+ static const char *proc_pid_get_link(struct dentry *dentry,
+                                    struct inode *inode,
+                                    struct delayed_call *done)
+ {
+       struct path path;
+-      int error = -EACCES;
++      int error;
+       if (!dentry)
+               return ERR_PTR(-ECHILD);
+-
+-      /* Are we allowed to snoop on the tasks file descriptors? */
+-      if (!proc_fd_access_allowed(inode))
+-              goto out;
+-
+-      error = PROC_I(inode)->op.proc_get_link(dentry, &path);
+-      if (error)
+-              goto out;
+-
+-      error = nd_jump_link(&path);
+-out:
++      error = call_proc_get_link(dentry, inode, &path);
++      if (!error)
++              error = nd_jump_link(&path);
+       return ERR_PTR(error);
+ }
+@@ -1849,17 +1835,11 @@ static int proc_pid_readlink(struct dent
+       struct inode *inode = d_inode(dentry);
+       struct path path;
+-      /* Are we allowed to snoop on the tasks file descriptors? */
+-      if (!proc_fd_access_allowed(inode))
+-              goto out;
+-
+-      error = PROC_I(inode)->op.proc_get_link(dentry, &path);
+-      if (error)
+-              goto out;
+-
+-      error = do_proc_readlink(&path, buffer, buflen);
+-      path_put(&path);
+-out:
++      error = call_proc_get_link(dentry, inode, &path);
++      if (!error) {
++              error = do_proc_readlink(&path, buffer, buflen);
++              path_put(&path);
++      }
+       return error;
+ }
+@@ -2250,21 +2230,16 @@ static const struct dentry_operations ti
+       .d_delete       = pid_delete_dentry,
+ };
+-static int map_files_get_link(struct dentry *dentry, struct path *path)
++static int map_files_get_link(struct dentry *dentry, struct path *path,
++                            struct task_struct *task)
+ {
+       unsigned long vm_start, vm_end;
+       struct vm_area_struct *vma;
+-      struct task_struct *task;
+       struct mm_struct *mm;
+       int rc;
+       rc = -ENOENT;
+-      task = get_proc_task(d_inode(dentry));
+-      if (!task)
+-              goto out;
+-
+       mm = get_task_mm(task);
+-      put_task_struct(task);
+       if (!mm)
+               goto out;
+--- a/fs/proc/fd.c
++++ b/fs/proc/fd.c
+@@ -171,24 +171,19 @@ static const struct dentry_operations ti
+       .d_delete       = pid_delete_dentry,
+ };
+-static int proc_fd_link(struct dentry *dentry, struct path *path)
++static int proc_fd_link(struct dentry *dentry, struct path *path,
++                      struct task_struct *task)
+ {
+-      struct task_struct *task;
+       int ret = -ENOENT;
++      unsigned int fd = proc_fd(d_inode(dentry));
++      struct file *fd_file;
+-      task = get_proc_task(d_inode(dentry));
+-      if (task) {
+-              unsigned int fd = proc_fd(d_inode(dentry));
+-              struct file *fd_file;
+-
+-              fd_file = fget_task(task, fd);
+-              if (fd_file) {
+-                      *path = fd_file->f_path;
+-                      path_get(&fd_file->f_path);
+-                      ret = 0;
+-                      fput(fd_file);
+-              }
+-              put_task_struct(task);
++      fd_file = fget_task(task, fd);
++      if (fd_file) {
++              *path = fd_file->f_path;
++              path_get(&fd_file->f_path);
++              ret = 0;
++              fput(fd_file);
+       }
+       return ret;
+--- a/fs/proc/internal.h
++++ b/fs/proc/internal.h
+@@ -107,7 +107,7 @@ extern struct kmem_cache *proc_dir_entry
+ void pde_free(struct proc_dir_entry *pde);
+ union proc_op {
+-      int (*proc_get_link)(struct dentry *, struct path *);
++      int (*proc_get_link)(struct dentry *, struct path *, struct task_struct *);
+       int (*proc_show)(struct seq_file *m,
+               struct pid_namespace *ns, struct pid *pid,
+               struct task_struct *task);
diff --git a/queue-6.18/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch b/queue-6.18/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch
new file mode 100644 (file)
index 0000000..1eb6073
--- /dev/null
@@ -0,0 +1,207 @@
+From stable+bounces-277029-greg=kroah.com@vger.kernel.org Fri Jul 17 13:19:01 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 07:18:34 -0400
+Subject: proc: protect ptrace_may_access() with exec_update_lock (part 1)
+To: stable@vger.kernel.org
+Cc: Jann Horn <jannh@google.com>, "Christian Brauner (Amutable)" <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717111835.1574543-3-sashal@kernel.org>
+
+From: Jann Horn <jannh@google.com>
+
+[ Upstream commit 6650527444dadc63d84aa939d14ecba4fadb2f69 ]
+
+Fix the easy cases where procfs currently calls ptrace_may_access() without
+exec_update_lock protection, where the fix is to simply add the extra lock
+or use mm_access():
+
+ - do_task_stat(): grab exec_update_lock
+ - proc_pid_wchan(): grab exec_update_lock
+ - proc_map_files_lookup(): use mm_access() instead of get_task_mm()
+ - proc_map_files_readdir(): use mm_access() instead of get_task_mm()
+ - proc_ns_get_link(): grab exec_update_lock
+ - proc_ns_readlink(): grab exec_update_lock
+
+Fixes: f83ce3e6b02d ("proc: avoid information leaks to non-privileged processes")
+Cc: stable@vger.kernel.org
+Signed-off-by: Jann Horn <jannh@google.com>
+Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-1-5c3d20e0ac33@google.com
+Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/array.c      |    6 ++++++
+ fs/proc/base.c       |   41 ++++++++++++++++++++++-------------------
+ fs/proc/namespaces.c |   12 ++++++++++++
+ 3 files changed, 40 insertions(+), 19 deletions(-)
+
+--- a/fs/proc/array.c
++++ b/fs/proc/array.c
+@@ -481,6 +481,11 @@ static int do_task_stat(struct seq_file
+       unsigned long flags;
+       int exit_code = task->exit_code;
+       struct signal_struct *sig = task->signal;
++      int ret;
++
++      ret = down_read_killable(&task->signal->exec_update_lock);
++      if (ret)
++              return ret;
+       state = *get_task_state(task);
+       vsize = eip = esp = 0;
+@@ -656,6 +661,7 @@ static int do_task_stat(struct seq_file
+               seq_puts(m, " 0");
+       seq_putc(m, '\n');
++      up_read(&task->signal->exec_update_lock);
+       if (mm)
+               mmput(mm);
+       return 0;
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -414,18 +414,24 @@ static int proc_pid_wchan(struct seq_fil
+ {
+       unsigned long wchan;
+       char symname[KSYM_NAME_LEN];
++      int err;
++      err = down_read_killable(&task->signal->exec_update_lock);
++      if (err)
++              return err;
+       if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
+               goto print0;
+       wchan = get_wchan(task);
+       if (wchan && !lookup_symbol_name(wchan, symname)) {
+               seq_puts(m, symname);
++              up_read(&task->signal->exec_update_lock);
+               return 0;
+       }
+ print0:
+       seq_putc(m, '0');
++      up_read(&task->signal->exec_update_lock);
+       return 0;
+ }
+ #endif /* CONFIG_KALLSYMS */
+@@ -2335,17 +2341,15 @@ static struct dentry *proc_map_files_loo
+       if (!task)
+               goto out;
+-      result = ERR_PTR(-EACCES);
+-      if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
+-              goto out_put_task;
+-
+       result = ERR_PTR(-ENOENT);
+       if (dname_to_vma_addr(dentry, &vm_start, &vm_end))
+               goto out_put_task;
+-      mm = get_task_mm(task);
+-      if (!mm)
++      mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
++      if (IS_ERR(mm)) {
++              result = ERR_CAST(mm);
+               goto out_put_task;
++      }
+       result = ERR_PTR(-EINTR);
+       if (mmap_read_lock_killable(mm))
+@@ -2395,23 +2399,22 @@ proc_map_files_readdir(struct file *file
+       if (!task)
+               goto out;
+-      ret = -EACCES;
+-      if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
+-              goto out_put_task;
+-
+       ret = 0;
+       if (!dir_emit_dots(file, ctx))
+               goto out_put_task;
+-      mm = get_task_mm(task);
+-      if (!mm)
++      mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
++      if (IS_ERR(mm)) {
++              ret = PTR_ERR(mm);
++              /* if the task has no mm, the directory should just be empty */
++              if (ret == -ESRCH)
++                      ret = 0;
+               goto out_put_task;
++      }
+       ret = mmap_read_lock_killable(mm);
+-      if (ret) {
+-              mmput(mm);
+-              goto out_put_task;
+-      }
++      if (ret)
++              goto out_put_mm;
+       nr_files = 0;
+@@ -2437,8 +2440,7 @@ proc_map_files_readdir(struct file *file
+               if (!p) {
+                       ret = -ENOMEM;
+                       mmap_read_unlock(mm);
+-                      mmput(mm);
+-                      goto out_put_task;
++                      goto out_put_mm;
+               }
+               p->start = vma->vm_start;
+@@ -2446,7 +2448,6 @@ proc_map_files_readdir(struct file *file
+               p->mode = vma->vm_file->f_mode;
+       }
+       mmap_read_unlock(mm);
+-      mmput(mm);
+       for (i = 0; i < nr_files; i++) {
+               char buf[4 * sizeof(long) + 2]; /* max: %lx-%lx\0 */
+@@ -2463,6 +2464,8 @@ proc_map_files_readdir(struct file *file
+               ctx->pos++;
+       }
++out_put_mm:
++      mmput(mm);
+ out_put_task:
+       put_task_struct(task);
+ out:
+--- a/fs/proc/namespaces.c
++++ b/fs/proc/namespaces.c
+@@ -55,6 +55,10 @@ static const char *proc_ns_get_link(stru
+       if (!task)
+               return ERR_PTR(-EACCES);
++      error = down_read_killable(&task->signal->exec_update_lock);
++      if (error)
++              goto out_put_task;
++
+       if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))
+               goto out;
+@@ -64,6 +68,8 @@ static const char *proc_ns_get_link(stru
+       error = nd_jump_link(&ns_path);
+ out:
++      up_read(&task->signal->exec_update_lock);
++out_put_task:
+       put_task_struct(task);
+       return ERR_PTR(error);
+ }
+@@ -80,11 +86,17 @@ static int proc_ns_readlink(struct dentr
+       if (!task)
+               return res;
++      res = down_read_killable(&task->signal->exec_update_lock);
++      if (res)
++              goto out_put_task;
++
+       if (ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS)) {
+               res = ns_get_name(name, sizeof(name), task, ns_ops);
+               if (res >= 0)
+                       res = readlink_copy(buffer, buflen, name, strlen(name));
+       }
++      up_read(&task->signal->exec_update_lock);
++out_put_task:
+       put_task_struct(task);
+       return res;
+ }
diff --git a/queue-6.18/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch b/queue-6.18/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch
new file mode 100644 (file)
index 0000000..c7d48e0
--- /dev/null
@@ -0,0 +1,204 @@
+From stable+bounces-276777-greg=kroah.com@vger.kernel.org Thu Jul 16 18:47:47 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 16 Jul 2026 12:45:13 -0400
+Subject: proc: rename proc_setattr to proc_nochmod_setattr
+To: stable@vger.kernel.org
+Cc: Christoph Hellwig <hch@lst.de>, Jan Kara <jack@suse.cz>, Christian Brauner <brauner@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260716164514.668766-1-sashal@kernel.org>
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 690005b0b1e6b567c88b7790e6d90d4d6c9e09cc ]
+
+What is currently proc_setattr is a special version added after the more
+general procfs ->seattr in commit 6d76fa58b050 ("Don't allow chmod() on
+the /proc/<pid>/ files").  Give it a name that reflects that to free the
+proc_setattr name and better describe what is doing.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Link: https://patch.msgid.link/20260325063711.3298685-5-hch@lst.de
+Reviewed-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/base.c       |   22 +++++++++++-----------
+ fs/proc/fd.c         |    6 +++---
+ fs/proc/internal.h   |    4 ++--
+ fs/proc/namespaces.c |    4 ++--
+ fs/proc/proc_net.c   |    2 +-
+ 5 files changed, 19 insertions(+), 19 deletions(-)
+
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -721,7 +721,7 @@ static bool proc_fd_access_allowed(struc
+       return allowed;
+ }
+-int proc_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
++int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+                struct iattr *attr)
+ {
+       int error;
+@@ -794,7 +794,7 @@ static int proc_pid_permission(struct mn
+ static const struct inode_operations proc_def_inode_operations = {
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static int proc_single_show(struct seq_file *m, void *v)
+@@ -1866,7 +1866,7 @@ out:
+ const struct inode_operations proc_pid_link_inode_operations = {
+       .readlink       = proc_pid_readlink,
+       .get_link       = proc_pid_get_link,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+@@ -2319,7 +2319,7 @@ proc_map_files_get_link(struct dentry *d
+ static const struct inode_operations proc_map_files_link_inode_operations = {
+       .readlink       = proc_pid_readlink,
+       .get_link       = proc_map_files_get_link,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static struct dentry *
+@@ -2398,7 +2398,7 @@ out:
+ static const struct inode_operations proc_map_files_inode_operations = {
+       .lookup         = proc_map_files_lookup,
+       .permission     = proc_fd_permission,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static int
+@@ -2885,7 +2885,7 @@ static struct dentry *proc_##LSM##_attr_
+ static const struct inode_operations proc_##LSM##_attr_dir_inode_ops = { \
+       .lookup         = proc_##LSM##_attr_dir_lookup, \
+       .getattr        = pid_getattr, \
+-      .setattr        = proc_setattr, \
++      .setattr        = proc_nochmod_setattr, \
+ }
+ #ifdef CONFIG_SECURITY_SMACK
+@@ -2944,7 +2944,7 @@ static struct dentry *proc_attr_dir_look
+ static const struct inode_operations proc_attr_dir_inode_operations = {
+       .lookup         = proc_attr_dir_lookup,
+       .getattr        = pid_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ #endif
+@@ -3460,7 +3460,7 @@ static struct dentry *proc_tgid_base_loo
+ static const struct inode_operations proc_tgid_base_inode_operations = {
+       .lookup         = proc_tgid_base_lookup,
+       .getattr        = pid_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+       .permission     = proc_pid_permission,
+ };
+@@ -3659,7 +3659,7 @@ static int proc_tid_comm_permission(stru
+ }
+ static const struct inode_operations proc_tid_comm_inode_operations = {
+-              .setattr        = proc_setattr,
++              .setattr        = proc_nochmod_setattr,
+               .permission     = proc_tid_comm_permission,
+ };
+@@ -3788,7 +3788,7 @@ static const struct file_operations proc
+ static const struct inode_operations proc_tid_base_inode_operations = {
+       .lookup         = proc_tid_base_lookup,
+       .getattr        = pid_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static struct dentry *proc_task_instantiate(struct dentry *dentry,
+@@ -4001,7 +4001,7 @@ static loff_t proc_dir_llseek(struct fil
+ static const struct inode_operations proc_task_inode_operations = {
+       .lookup         = proc_task_lookup,
+       .getattr        = proc_task_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+       .permission     = proc_pid_permission,
+ };
+--- a/fs/proc/fd.c
++++ b/fs/proc/fd.c
+@@ -102,7 +102,7 @@ static int proc_fdinfo_permission(struct
+ static const struct inode_operations proc_fdinfo_file_inode_operations = {
+       .permission     = proc_fdinfo_permission,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static const struct file_operations proc_fdinfo_file_operations = {
+@@ -361,7 +361,7 @@ const struct inode_operations proc_fd_in
+       .lookup         = proc_lookupfd,
+       .permission     = proc_fd_permission,
+       .getattr        = proc_fd_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry,
+@@ -402,7 +402,7 @@ static int proc_fdinfo_iterate(struct fi
+ const struct inode_operations proc_fdinfo_inode_operations = {
+       .lookup         = proc_lookupfdinfo,
+       .permission     = proc_fdinfo_permission,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ const struct file_operations proc_fdinfo_operations = {
+--- a/fs/proc/internal.h
++++ b/fs/proc/internal.h
+@@ -257,8 +257,8 @@ extern int proc_pid_statm(struct seq_fil
+ extern const struct dentry_operations pid_dentry_operations;
+ extern int pid_getattr(struct mnt_idmap *, const struct path *,
+                      struct kstat *, u32, unsigned int);
+-extern int proc_setattr(struct mnt_idmap *, struct dentry *,
+-                      struct iattr *);
++int proc_nochmod_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
++                      struct iattr *attr);
+ extern void proc_pid_evict_inode(struct proc_inode *);
+ extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *, umode_t);
+ extern void pid_update_inode(struct task_struct *, struct inode *);
+--- a/fs/proc/namespaces.c
++++ b/fs/proc/namespaces.c
+@@ -92,7 +92,7 @@ static int proc_ns_readlink(struct dentr
+ static const struct inode_operations proc_ns_link_inode_operations = {
+       .readlink       = proc_ns_readlink,
+       .get_link       = proc_ns_get_link,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static struct dentry *proc_ns_instantiate(struct dentry *dentry,
+@@ -178,5 +178,5 @@ out_no_task:
+ const struct inode_operations proc_ns_dir_inode_operations = {
+       .lookup         = proc_ns_dir_lookup,
+       .getattr        = pid_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+--- a/fs/proc/proc_net.c
++++ b/fs/proc/proc_net.c
+@@ -322,7 +322,7 @@ static int proc_tgid_net_getattr(struct
+ const struct inode_operations proc_net_inode_operations = {
+       .lookup         = proc_tgid_net_lookup,
+       .getattr        = proc_tgid_net_getattr,
+-      .setattr        = proc_setattr,
++      .setattr        = proc_nochmod_setattr,
+ };
+ static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx)
diff --git a/queue-6.18/seqlock-change-do_task_stat-to-use-scoped_seqlock_read.patch b/queue-6.18/seqlock-change-do_task_stat-to-use-scoped_seqlock_read.patch
new file mode 100644 (file)
index 0000000..e455b05
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-277028-greg=kroah.com@vger.kernel.org Fri Jul 17 13:18:51 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 07:18:33 -0400
+Subject: seqlock: Change do_task_stat() to use scoped_seqlock_read()
+To: stable@vger.kernel.org
+Cc: Oleg Nesterov <oleg@redhat.com>, "Peter Zijlstra (Intel)" <peterz@infradead.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717111835.1574543-2-sashal@kernel.org>
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+[ Upstream commit b76f72bea2c601afec81829ea427fc0d20f83216 ]
+
+To simplify the code and make it more readable.
+
+[peterz: change to new interface]
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Stable-dep-of: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/array.c |    9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/fs/proc/array.c
++++ b/fs/proc/array.c
+@@ -481,7 +481,6 @@ static int do_task_stat(struct seq_file
+       unsigned long flags;
+       int exit_code = task->exit_code;
+       struct signal_struct *sig = task->signal;
+-      unsigned int seq = 1;
+       state = *get_task_state(task);
+       vsize = eip = esp = 0;
+@@ -538,10 +537,7 @@ static int do_task_stat(struct seq_file
+       if (permitted && (!whole || num_threads < 2))
+               wchan = !task_is_running(task);
+-      do {
+-              seq++; /* 2 on the 1st/lockless path, otherwise odd */
+-              flags = read_seqbegin_or_lock_irqsave(&sig->stats_lock, &seq);
+-
++      scoped_seqlock_read (&sig->stats_lock, ss_lock_irqsave) {
+               cmin_flt = sig->cmin_flt;
+               cmaj_flt = sig->cmaj_flt;
+               cutime = sig->cutime;
+@@ -563,8 +559,7 @@ static int do_task_stat(struct seq_file
+                       }
+                       rcu_read_unlock();
+               }
+-      } while (need_seqretry(&sig->stats_lock, seq));
+-      done_seqretry_irqrestore(&sig->stats_lock, seq, flags);
++      }
+       if (whole) {
+               thread_group_cputime_adjusted(task, &utime, &stime);
diff --git a/queue-6.18/seqlock-introduce-scoped_seqlock_read.patch b/queue-6.18/seqlock-introduce-scoped_seqlock_read.patch
new file mode 100644 (file)
index 0000000..1e1ae3a
--- /dev/null
@@ -0,0 +1,164 @@
+From stable+bounces-277027-greg=kroah.com@vger.kernel.org Fri Jul 17 13:42:38 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 17 Jul 2026 07:18:32 -0400
+Subject: seqlock: Introduce scoped_seqlock_read()
+To: stable@vger.kernel.org
+Cc: Peter Zijlstra <peterz@infradead.org>, Oleg Nesterov <oleg@redhat.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260717111835.1574543-1-sashal@kernel.org>
+
+From: Peter Zijlstra <peterz@infradead.org>
+
+[ Upstream commit cc39f3872c0865bef992b713338df369554fa9e0 ]
+
+The read_seqbegin/need_seqretry/done_seqretry API is cumbersome and
+error prone. With the new helper the "typical" code like
+
+       int seq, nextseq;
+       unsigned long flags;
+
+       nextseq = 0;
+       do {
+               seq = nextseq;
+               flags = read_seqbegin_or_lock_irqsave(&seqlock, &seq);
+
+               // read-side critical section
+
+               nextseq = 1;
+       } while (need_seqretry(&seqlock, seq));
+       done_seqretry_irqrestore(&seqlock, seq, flags);
+
+can be rewritten as
+
+       scoped_seqlock_read (&seqlock, ss_lock_irqsave) {
+               // read-side critical section
+       }
+
+Original idea by Oleg Nesterov; with contributions from Linus.
+
+Originally-by: Oleg Nesterov <oleg@redhat.com>
+Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
+Stable-dep-of: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/seqlock.h |  111 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 111 insertions(+)
+
+--- a/include/linux/seqlock.h
++++ b/include/linux/seqlock.h
+@@ -1209,4 +1209,115 @@ done_seqretry_irqrestore(seqlock_t *lock
+       if (seq & 1)
+               read_sequnlock_excl_irqrestore(lock, flags);
+ }
++
++enum ss_state {
++      ss_done = 0,
++      ss_lock,
++      ss_lock_irqsave,
++      ss_lockless,
++};
++
++struct ss_tmp {
++      enum ss_state   state;
++      unsigned long   data;
++      spinlock_t      *lock;
++      spinlock_t      *lock_irqsave;
++};
++
++static inline void __scoped_seqlock_cleanup(struct ss_tmp *sst)
++{
++      if (sst->lock)
++              spin_unlock(sst->lock);
++      if (sst->lock_irqsave)
++              spin_unlock_irqrestore(sst->lock_irqsave, sst->data);
++}
++
++extern void __scoped_seqlock_invalid_target(void);
++
++#if defined(CONFIG_CC_IS_GCC) && CONFIG_GCC_VERSION < 90000
++/*
++ * For some reason some GCC-8 architectures (nios2, alpha) have trouble
++ * determining that the ss_done state is impossible in __scoped_seqlock_next()
++ * below.
++ */
++static inline void __scoped_seqlock_bug(void) { }
++#else
++/*
++ * Canary for compiler optimization -- if the compiler doesn't realize this is
++ * an impossible state, it very likely generates sub-optimal code here.
++ */
++extern void __scoped_seqlock_bug(void);
++#endif
++
++static inline void
++__scoped_seqlock_next(struct ss_tmp *sst, seqlock_t *lock, enum ss_state target)
++{
++      switch (sst->state) {
++      case ss_done:
++              __scoped_seqlock_bug();
++              return;
++
++      case ss_lock:
++      case ss_lock_irqsave:
++              sst->state = ss_done;
++              return;
++
++      case ss_lockless:
++              if (!read_seqretry(lock, sst->data)) {
++                      sst->state = ss_done;
++                      return;
++              }
++              break;
++      }
++
++      switch (target) {
++      case ss_done:
++              __scoped_seqlock_invalid_target();
++              return;
++
++      case ss_lock:
++              sst->lock = &lock->lock;
++              spin_lock(sst->lock);
++              sst->state = ss_lock;
++              return;
++
++      case ss_lock_irqsave:
++              sst->lock_irqsave = &lock->lock;
++              spin_lock_irqsave(sst->lock_irqsave, sst->data);
++              sst->state = ss_lock_irqsave;
++              return;
++
++      case ss_lockless:
++              sst->data = read_seqbegin(lock);
++              return;
++      }
++}
++
++#define __scoped_seqlock_read(_seqlock, _target, _s)                  \
++      for (struct ss_tmp _s __cleanup(__scoped_seqlock_cleanup) =     \
++           { .state = ss_lockless, .data = read_seqbegin(_seqlock) }; \
++           _s.state != ss_done;                                       \
++           __scoped_seqlock_next(&_s, _seqlock, _target))
++
++/**
++ * scoped_seqlock_read (lock, ss_state) - execute the read side critical
++ *                                        section without manual sequence
++ *                                        counter handling or calls to other
++ *                                        helpers
++ * @lock: pointer to seqlock_t protecting the data
++ * @ss_state: one of {ss_lock, ss_lock_irqsave, ss_lockless} indicating
++ *            the type of critical read section
++ *
++ * Example:
++ *
++ *     scoped_seqlock_read (&lock, ss_lock) {
++ *         // read-side critical section
++ *     }
++ *
++ * Starts with a lockess pass first. If it fails, restarts the critical
++ * section with the lock held.
++ */
++#define scoped_seqlock_read(_seqlock, _target)                                \
++      __scoped_seqlock_read(_seqlock, _target, __UNIQUE_ID(seqlock))
++
+ #endif /* __LINUX_SEQLOCK_H */
index 42ada9ade2663beb7b28ac8c27435a5b8d86caf1..236af89c162cf1d93fef0de833ddb3f6e6639aad 100644 (file)
@@ -1516,3 +1516,79 @@ mmc-sdhci-esdhc-imx-use-pm_runtime_resume_and_get-in-suspend.patch
 mmc-sdhci-esdhc-imx-make-non-fatal-errors-non-blocking-in-suspend.patch
 mmc-sdhci-esdhc-imx-fix-resume-error-handling.patch
 crypto-xilinx-trng-remove-crypto_rng-interface.patch
+acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch
+acpi-driver-check-acpi_companion-against-null-during-probe.patch
+acpi-bus-introduce-devm_acpi_install_notify_handler.patch
+acpi-nfit-core-use-devm_acpi_install_notify_handler.patch
+acpi-nfit-core-fix-possible-deadlock-and-missing-notifications.patch
+iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch
+alsa-scarlett2-allow-selecting-config_set-by-firmware-version.patch
+alsa-scarlett2-update-offsets-for-2i2-gen-4-firmware-2417.patch
+firmware_loader-add-cancel-helper-for-async-requests.patch
+alsa-hda-tas2781-cancel-async-firmware-request-at-unbind.patch
+vfio-mlx5-fix-racy-bitfields-and-tighten-struct-layout.patch
+binder-use-list_head-to-initialize-on-stack-list-head.patch
+binder-cache-secctx-size-before-release-zeroes-it.patch
+pci-imx6-fix-reference-clock-source-selection-for-i.mx95.patch
+pci-imx6-configure-ref_use_pad-before-phy-reset-for-i.mx95.patch
+pci-iov-adjust-barsz-when-changing-bar-size.patch
+pci-change-pci_dev-variable-from-bridge-to-dev.patch
+pci-try-bar-resize-even-when-no-window-was-released.patch
+pci-free-saved-list-without-holding-pci_bus_sem.patch
+pci-fix-restoring-bars-on-bar-resize-rollback-path.patch
+pci-add-kerneldoc-for-pci_resize_resource.patch
+pci-move-resizable-bar-code-to-rebar.c.patch
+pci-skip-resizable-bar-restore-on-read-error.patch
+staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch
+staging-rtl8723bs-fix-spaces-around-binary-operators.patch
+staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch
+crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch
+media-nxp-imx8-isi-use-devm_pm_runtime_enable-to-simplify-code.patch
+media-nxp-imx8-isi-fix-use-after-free-on-remove.patch
+bluetooth-6lowpan-fix-cyclic-locking-warning-on-netdev-unregister.patch
+bluetooth-l2cap-fix-use-after-free-in-l2cap_sock_new_connection_cb.patch
+cifs-smb1-split-rename-cifstransport.c.patch
+cifs-smb1-split-add-some-includes.patch
+ksmbd-use-opener-credentials-for-fsctl-mutations.patch
+ksmbd-fix-path-resolution-in-ksmbd_vfs_kern_path_create.patch
+ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch
+ksmbd-track-the-connection-owning-a-byte-range-lock.patch
+ksmbd-use-sha-512-library-for-smb3.1.1-preauth-hash.patch
+ksmbd-use-hmac-sha256-library-for-message-signing-and-key-generation.patch
+ksmbd-use-hmac-md5-library-for-ntlmv2.patch
+ksmbd-validate-ntlmv2-response-before-updating-session-key.patch
+proc-rename-proc_setattr-to-proc_nochmod_setattr.patch
+proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch
+seqlock-introduce-scoped_seqlock_read.patch
+seqlock-change-do_task_stat-to-use-scoped_seqlock_read.patch
+proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch
+hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch
+hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch
+xfs-initialize-iomap-flags-earlier-in-xfs_bmbt_to_iomap.patch
+bpf-consistently-use-reg_state-for-register-access-in-the-verifier.patch
+bpf-introduce-struct-bpf_map_desc-in-verifier.patch
+bpf-keep-dynamic-inner-array-lookups-nullable.patch
+bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch
+bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch
+xfs-add-a-xfs_groups_to_rfsbs-helper.patch
+xfs-only-log-freed-extents-for-the-current-rtg-in-zoned-growfs.patch
+xfs-use-bio_reuse-in-the-zone-gc-code.patch
+usb-dwc3-support-usb3340x-ulpi-phy-high-speed-negotiation.patch
+usb-dwc3-fix-dwc3_readl-and-dwc3_writel-calls-in-dwc3_ulpi_setup.patch
+usb-atm-ueagle-atm-use-dev_dbg-for-device-found-message.patch
+usb-atm-ueagle-atm-remove-function-entry-exit-debug-messages.patch
+usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch
+new-helper-simple_remove_by_name.patch
+functionfs-don-t-abuse-ffs_data_closed-on-fs-shutdown.patch
+functionfs-don-t-bother-with-ffs-ref-in-ffs_data_-opened-closed.patch
+functionfs-switch-to-simple_remove_by_name.patch
+functionfs-use-spinlock-for-ffs_deactivated-ffs_closing-transitions.patch
+usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch
+usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch
+crypto-atmel-sha204a-fail-on-hwrng-registration-error-in-probe-path.patch
+btrfs-concentrate-the-error-handling-of-submit_one_sector.patch
+btrfs-replace-for_each_set_bit-with-for_each_set_bitmap.patch
+btrfs-remove-folio-parameter-from-ordered-io-related-functions.patch
+crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch
+btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch
+btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch
diff --git a/queue-6.18/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch b/queue-6.18/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch
new file mode 100644 (file)
index 0000000..0d391e2
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-274617-greg=kroah.com@vger.kernel.org Wed Jul 15 02:07:44 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 20:07:34 -0400
+Subject: staging: rtl8723bs: core: move constants to right side in comparison
+To: stable@vger.kernel.org
+Cc: William Hansen-Baird <william.hansen.baird@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715000736.3730201-1-sashal@kernel.org>
+
+From: William Hansen-Baird <william.hansen.baird@gmail.com>
+
+[ Upstream commit cf0f2680c30d4b438a5e84b73dc70be405936b54 ]
+
+Move constants to right side in if-statement conditions.
+
+Signed-off-by: William Hansen-Baird <william.hansen.baird@gmail.com>
+Link: https://patch.msgid.link/20251224100329.762141-3-william.hansen.baird@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/core/rtw_ieee80211.c |    4 ++--
+ drivers/staging/rtl8723bs/core/rtw_security.c  |    2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+@@ -1012,7 +1012,7 @@ static int rtw_get_cipher_info(struct wl
+       pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
+       if (pbuf && (wpa_ielen > 0)) {
+-              if (_SUCCESS == rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) {
++              if (rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) {
+                       pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
+                       pnetwork->bcn_info.group_cipher = group_cipher;
+                       pnetwork->bcn_info.is_8021x = is8021x;
+@@ -1022,7 +1022,7 @@ static int rtw_get_cipher_info(struct wl
+               pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
+               if (pbuf && (wpa_ielen > 0)) {
+-                      if (_SUCCESS == rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x)) {
++                      if (rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) {
+                               pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
+                               pnetwork->bcn_info.group_cipher = group_cipher;
+                               pnetwork->bcn_info.is_8021x = is8021x;
+--- a/drivers/staging/rtl8723bs/core/rtw_security.c
++++ b/drivers/staging/rtl8723bs/core/rtw_security.c
+@@ -1495,7 +1495,7 @@ void rtw_sec_restore_wep_key(struct adap
+       struct security_priv *securitypriv = &(adapter->securitypriv);
+       signed int keyid;
+-      if ((_WEP40_ == securitypriv->dot11PrivacyAlgrthm) || (_WEP104_ == securitypriv->dot11PrivacyAlgrthm)) {
++      if ((securitypriv->dot11PrivacyAlgrthm == _WEP40_) || (securitypriv->dot11PrivacyAlgrthm == _WEP104_)) {
+               for (keyid = 0; keyid < 4; keyid++) {
+                       if (securitypriv->key_mask & BIT(keyid)) {
+                               if (keyid == securitypriv->dot11PrivacyKeyIndex)
diff --git a/queue-6.18/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch b/queue-6.18/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch
new file mode 100644 (file)
index 0000000..638c7d6
--- /dev/null
@@ -0,0 +1,94 @@
+From stable+bounces-274619-greg=kroah.com@vger.kernel.org Wed Jul 15 02:07:49 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 20:07:36 -0400
+Subject: staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()
+To: stable@vger.kernel.org
+Cc: Alexandru Hossu <hossu.alexandru@gmail.com>, stable <stable@kernel.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715000736.3730201-3-sashal@kernel.org>
+
+From: Alexandru Hossu <hossu.alexandru@gmail.com>
+
+[ Upstream commit 1463ca3ec6601cbb097d8d87dbf5dcf1cb86a344 ]
+
+Three IE/attribute parsing functions have missing bounds checks.
+
+rtw_get_sec_ie() and rtw_get_wapi_ie() iterate over a raw IE buffer
+without verifying that the header bytes (tag + length) are within the
+remaining buffer before reading them.  Additionally, rtw_get_sec_ie()
+compares the 4-byte WPA OUI at cnt+2 without checking that at least
+6 bytes remain, and rtw_get_wapi_ie() compares a 4-byte WAPI OUI at
+cnt+6 without checking that at least 10 bytes remain.
+
+rtw_get_wps_attr() reads wps_ie[0] and wps_ie+2 unconditionally at
+entry, before verifying that wps_ielen is large enough to contain
+the 6-byte WPS IE header (element_id + length + 4-byte OUI).  Inside
+the attribute loop, get_unaligned_be16() is called on attr_ptr and
+attr_ptr+2 without checking that 4 bytes remain in the buffer.
+
+Add a cnt+2 bounds check before each loop body in rtw_get_sec_ie()
+and rtw_get_wapi_ie(), guard each multi-byte comparison with a minimum
+IE length requirement, add a wps_ielen < 6 early return in
+rtw_get_wps_attr(), and add a 4-byte bounds check in its inner loop.
+
+Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Alexandru Hossu <hossu.alexandru@gmail.com>
+Link: https://patch.msgid.link/20260522004531.1038924-8-hossu.alexandru@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/core/rtw_ieee80211.c |   15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+@@ -585,9 +585,14 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_l
+       cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
+       while (cnt < in_len) {
++              if (cnt + 2 > in_len)
++                      break;
++              if (cnt + 2 + in_ie[cnt + 1] > in_len)
++                      break;
+               authmode = in_ie[cnt];
+               if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY &&
++                  in_ie[cnt + 1] >= 8 &&
+                   (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) ||
+                    !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) {
+                       if (wapi_ie)
+@@ -620,9 +625,14 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_l
+       cnt = (_TIMESTAMP_ + _BEACON_ITERVAL_ + _CAPABILITY_);
+       while (cnt < in_len) {
++              if (cnt + 2 > in_len)
++                      break;
++              if (cnt + 2 + in_ie[cnt + 1] > in_len)
++                      break;
+               authmode = in_ie[cnt];
+               if ((authmode == WLAN_EID_VENDOR_SPECIFIC) &&
++                  in_ie[cnt + 1] >= 4 &&
+                   (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) {
+                       if (wpa_ie)
+                               memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+@@ -707,6 +717,9 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wp
+       if (len_attr)
+               *len_attr = 0;
++      if (wps_ielen < 6)
++              return attr_ptr;
++
+       if ((wps_ie[0] != WLAN_EID_VENDOR_SPECIFIC) ||
+               (memcmp(wps_ie + 2, wps_oui, 4))) {
+               return attr_ptr;
+@@ -717,6 +730,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wp
+       while (attr_ptr - wps_ie < wps_ielen) {
+               /*  4 = 2(Attribute ID) + 2(Length) */
++              if (attr_ptr + 4 > wps_ie + wps_ielen)
++                      break;
+               u16 attr_id = get_unaligned_be16(attr_ptr);
+               u16 attr_data_len = get_unaligned_be16(attr_ptr + 2);
+               u16 attr_len = attr_data_len + 4;
diff --git a/queue-6.18/staging-rtl8723bs-fix-spaces-around-binary-operators.patch b/queue-6.18/staging-rtl8723bs-fix-spaces-around-binary-operators.patch
new file mode 100644 (file)
index 0000000..bc8a8b6
--- /dev/null
@@ -0,0 +1,206 @@
+From stable+bounces-274618-greg=kroah.com@vger.kernel.org Wed Jul 15 02:07:47 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 20:07:35 -0400
+Subject: staging: rtl8723bs: fix spaces around binary operators
+To: stable@vger.kernel.org
+Cc: Nikolay Kulikov <nikolayof23@gmail.com>, Ethan Tidmore <ethantidmore06@gmail.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260715000736.3730201-2-sashal@kernel.org>
+
+From: Nikolay Kulikov <nikolayof23@gmail.com>
+
+[ Upstream commit 0c9d1b56f9af0762a5be5118e1bb962f23784dc4 ]
+
+Add missing spaces and fix line length to comply with kernel coding
+style.
+
+Signed-off-by: Nikolay Kulikov <nikolayof23@gmail.com>
+Reviewed-by: Ethan Tidmore <ethantidmore06@gmail.com>
+Link: https://patch.msgid.link/20260221172751.52329-1-nikolayof23@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 1463ca3ec660 ("staging: rtl8723bs: fix OOB reads in rtw_get_sec_ie(), rtw_get_wapi_ie(), and rtw_get_wps_attr()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/staging/rtl8723bs/core/rtw_ieee80211.c |   78 +++++++++++++------------
+ 1 file changed, 42 insertions(+), 36 deletions(-)
+
+--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c
+@@ -456,10 +456,10 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa
+               return _FAIL;
+       }
+-      if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) || (*(wpa_ie+1) != (u8)(wpa_ie_len - 2)) ||
+-         (memcmp(wpa_ie+2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN))) {
++      if ((*wpa_ie != WLAN_EID_VENDOR_SPECIFIC) ||
++          (*(wpa_ie + 1) != (u8)(wpa_ie_len - 2)) ||
++          (memcmp(wpa_ie + 2, RTW_WPA_OUI_TYPE, WPA_SELECTOR_LEN)))
+               return _FAIL;
+-      }
+       pos = wpa_ie;
+@@ -519,7 +519,7 @@ int rtw_parse_wpa2_ie(u8 *rsn_ie, int rs
+               return _FAIL;
+       }
+-      if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie+1) != (u8)(rsn_ie_len - 2)))
++      if ((*rsn_ie != WLAN_EID_RSN) || (*(rsn_ie + 1) != (u8)(rsn_ie_len - 2)))
+               return _FAIL;
+       pos = rsn_ie;
+@@ -587,18 +587,18 @@ int rtw_get_wapi_ie(u8 *in_ie, uint in_l
+       while (cnt < in_len) {
+               authmode = in_ie[cnt];
+-              /* if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY) */
+-              if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY && (!memcmp(&in_ie[cnt+6], wapi_oui1, 4) ||
+-                                      !memcmp(&in_ie[cnt+6], wapi_oui2, 4))) {
++              if (authmode == WLAN_EID_BSS_AC_ACCESS_DELAY &&
++                  (!memcmp(&in_ie[cnt + 6], wapi_oui1, 4) ||
++                   !memcmp(&in_ie[cnt + 6], wapi_oui2, 4))) {
+                       if (wapi_ie)
+-                              memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt+1]+2);
++                              memcpy(wapi_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+                       if (wapi_len)
+-                              *wapi_len = in_ie[cnt+1]+2;
++                              *wapi_len = in_ie[cnt + 1] + 2;
+-                      cnt += in_ie[cnt+1]+2;  /* get next */
++                      cnt += in_ie[cnt + 1] + 2;  /* get next */
+               } else {
+-                      cnt += in_ie[cnt+1]+2;   /* get next */
++                      cnt += in_ie[cnt + 1] + 2;   /* get next */
+               }
+       }
+@@ -622,9 +622,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_l
+       while (cnt < in_len) {
+               authmode = in_ie[cnt];
+-              if ((authmode == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], &wpa_oui[0], 4))) {
++              if ((authmode == WLAN_EID_VENDOR_SPECIFIC) &&
++                  (!memcmp(&in_ie[cnt + 2], &wpa_oui[0], 4))) {
+                       if (wpa_ie)
+-                              memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt+1]+2);
++                              memcpy(wpa_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+                       *wpa_len = in_ie[cnt + 1] + 2;
+                       cnt += in_ie[cnt + 1] + 2;  /* get next */
+@@ -633,10 +634,10 @@ void rtw_get_sec_ie(u8 *in_ie, uint in_l
+                               if (rsn_ie)
+                                       memcpy(rsn_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+-                              *rsn_len = in_ie[cnt+1]+2;
+-                              cnt += in_ie[cnt+1]+2;  /* get next */
++                              *rsn_len = in_ie[cnt + 1] + 2;
++                              cnt += in_ie[cnt + 1] + 2;  /* get next */
+                       } else {
+-                              cnt += in_ie[cnt+1]+2;   /* get next */
++                              cnt += in_ie[cnt + 1] + 2;   /* get next */
+                       }
+               }
+       }
+@@ -668,20 +669,20 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_le
+       while (cnt < in_len) {
+               eid = in_ie[cnt];
+-              if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt+2], wps_oui, 4))) {
++              if ((eid == WLAN_EID_VENDOR_SPECIFIC) && (!memcmp(&in_ie[cnt + 2], wps_oui, 4))) {
+                       wpsie_ptr = &in_ie[cnt];
+                       if (wps_ie)
+-                              memcpy(wps_ie, &in_ie[cnt], in_ie[cnt+1]+2);
++                              memcpy(wps_ie, &in_ie[cnt], in_ie[cnt + 1] + 2);
+                       if (wps_ielen)
+-                              *wps_ielen = in_ie[cnt+1]+2;
++                              *wps_ielen = in_ie[cnt + 1] + 2;
+-                      cnt += in_ie[cnt+1]+2;
++                      cnt += in_ie[cnt + 1] + 2;
+                       break;
+               }
+-              cnt += in_ie[cnt+1]+2; /* goto next */
++              cnt += in_ie[cnt + 1] + 2; /* goto next */
+       }
+       return wpsie_ptr;
+@@ -759,12 +760,12 @@ u8 *rtw_get_wps_attr_content(u8 *wps_ie,
+       if (attr_ptr && attr_len) {
+               if (buf_content)
+-                      memcpy(buf_content, attr_ptr+4, attr_len-4);
++                      memcpy(buf_content, attr_ptr + 4, attr_len - 4);
+               if (len_content)
+-                      *len_content = attr_len-4;
++                      *len_content = attr_len - 4;
+-              return attr_ptr+4;
++              return attr_ptr + 4;
+       }
+       return NULL;
+@@ -1009,20 +1010,25 @@ static int rtw_get_cipher_info(struct wl
+       int group_cipher = 0, pairwise_cipher = 0, is8021x = 0;
+       int ret = _FAIL;
+-      pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
++      pbuf = rtw_get_wpa_ie(&pnetwork->network.ies[12],
++                            &wpa_ielen,
++                            pnetwork->network.ie_length - 12);
+       if (pbuf && (wpa_ielen > 0)) {
+-              if (rtw_parse_wpa_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) {
++              if (rtw_parse_wpa_ie(pbuf, wpa_ielen + 2, &group_cipher,
++                                   &pairwise_cipher, &is8021x) == _SUCCESS) {
+                       pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
+                       pnetwork->bcn_info.group_cipher = group_cipher;
+                       pnetwork->bcn_info.is_8021x = is8021x;
+                       ret = _SUCCESS;
+               }
+       } else {
+-              pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen, pnetwork->network.ie_length-12);
++              pbuf = rtw_get_wpa2_ie(&pnetwork->network.ies[12], &wpa_ielen,
++                                     pnetwork->network.ie_length - 12);
+               if (pbuf && (wpa_ielen > 0)) {
+-                      if (rtw_parse_wpa2_ie(pbuf, wpa_ielen+2, &group_cipher, &pairwise_cipher, &is8021x) == _SUCCESS) {
++                      if (rtw_parse_wpa2_ie(pbuf, wpa_ielen + 2, &group_cipher,
++                                            &pairwise_cipher, &is8021x) == _SUCCESS) {
+                               pnetwork->bcn_info.pairwise_cipher = pairwise_cipher;
+                               pnetwork->bcn_info.group_cipher = group_cipher;
+                               pnetwork->bcn_info.is_8021x = is8021x;
+@@ -1091,21 +1097,21 @@ u16 rtw_mcs_rate(u8 bw_40MHz, u8 short_G
+       u16 max_rate = 0;
+       if (MCS_rate[0] & BIT(7))
+-              max_rate = (bw_40MHz) ? ((short_GI)?1500:1350):((short_GI)?722:650);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 1500 : 1350) : ((short_GI) ? 722 : 650);
+       else if (MCS_rate[0] & BIT(6))
+-              max_rate = (bw_40MHz) ? ((short_GI)?1350:1215):((short_GI)?650:585);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 1350 : 1215) : ((short_GI) ? 650 : 585);
+       else if (MCS_rate[0] & BIT(5))
+-              max_rate = (bw_40MHz) ? ((short_GI)?1200:1080):((short_GI)?578:520);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 1200 : 1080) : ((short_GI) ? 578 : 520);
+       else if (MCS_rate[0] & BIT(4))
+-              max_rate = (bw_40MHz) ? ((short_GI)?900:810):((short_GI)?433:390);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 900 : 810) : ((short_GI) ? 433 : 390);
+       else if (MCS_rate[0] & BIT(3))
+-              max_rate = (bw_40MHz) ? ((short_GI)?600:540):((short_GI)?289:260);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 600 : 540) : ((short_GI) ? 289 : 260);
+       else if (MCS_rate[0] & BIT(2))
+-              max_rate = (bw_40MHz) ? ((short_GI)?450:405):((short_GI)?217:195);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 450 : 405) : ((short_GI) ? 217 : 195);
+       else if (MCS_rate[0] & BIT(1))
+-              max_rate = (bw_40MHz) ? ((short_GI)?300:270):((short_GI)?144:130);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 300 : 270) : ((short_GI) ? 144 : 130);
+       else if (MCS_rate[0] & BIT(0))
+-              max_rate = (bw_40MHz) ? ((short_GI)?150:135):((short_GI)?72:65);
++              max_rate = (bw_40MHz) ? ((short_GI) ? 150 : 135) : ((short_GI) ? 72 : 65);
+       return max_rate;
+ }
diff --git a/queue-6.18/usb-atm-ueagle-atm-remove-function-entry-exit-debug-messages.patch b/queue-6.18/usb-atm-ueagle-atm-remove-function-entry-exit-debug-messages.patch
new file mode 100644 (file)
index 0000000..e91fbf4
--- /dev/null
@@ -0,0 +1,337 @@
+From stable+bounces-277438-greg=kroah.com@vger.kernel.org Sun Jul 19 14:43:59 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 08:43:47 -0400
+Subject: usb: atm: ueagle-atm: remove function entry/exit debug messages
+To: stable@vger.kernel.org
+Cc: Mauricio Faria de Oliveira <mfo@igalia.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Stanislaw Gruszka <stf_xl@wp.pl>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260719124348.3646001-4-sashal@kernel.org>
+
+From: Mauricio Faria de Oliveira <mfo@igalia.com>
+
+[ Upstream commit 1414b2242c2e9d670c4d8b83480f13db57627ba9 ]
+
+Remove the driver-internal function entry/exit debug messages
+in favor of existing kernel-level function tracing mechanisms.
+
+Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Link: https://lore.kernel.org/all/2026051657-scruffy-embark-45ea@gregkh/
+Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
+Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
+Link: https://patch.msgid.link/20260520-ueagle-atm-cleanup-v1-2-010c8bc7b214@igalia.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/atm/ueagle-atm.c |   56 ++++---------------------------------------
+ 1 file changed, 6 insertions(+), 50 deletions(-)
+
+--- a/drivers/usb/atm/ueagle-atm.c
++++ b/drivers/usb/atm/ueagle-atm.c
+@@ -50,12 +50,6 @@
+                               "[ueagle-atm vdbg]  " format, ##args); \
+       } while (0)
+-#define uea_enters(usb_dev) \
+-      uea_vdbg(usb_dev, "entering %s\n" , __func__)
+-
+-#define uea_leaves(usb_dev) \
+-      uea_vdbg(usb_dev, "leaving  %s\n" , __func__)
+-
+ #define uea_err(usb_dev, format, args...) \
+       dev_err(&(usb_dev)->dev , "[UEAGLE-ATM] " format , ##args)
+@@ -605,7 +599,6 @@ static void uea_upload_pre_firmware(cons
+       u32 crc = 0;
+       int ret, size;
+-      uea_enters(usb);
+       if (!fw_entry) {
+               uea_err(usb, "firmware is not available\n");
+               goto err;
+@@ -669,7 +662,6 @@ err_fw_corrupted:
+       uea_err(usb, "firmware is corrupted\n");
+ err:
+       release_firmware(fw_entry);
+-      uea_leaves(usb);
+ }
+ /*
+@@ -680,7 +672,6 @@ static int uea_load_firmware(struct usb_
+       int ret;
+       char *fw_name = EAGLE_FIRMWARE;
+-      uea_enters(usb);
+       uea_info(usb, "pre-firmware device, uploading firmware\n");
+       switch (ver) {
+@@ -709,7 +700,6 @@ static int uea_load_firmware(struct usb_
+       else
+               uea_info(usb, "loading firmware %s\n", fw_name);
+-      uea_leaves(usb);
+       return ret;
+ }
+@@ -1136,7 +1126,6 @@ static int uea_cmv_e1(struct uea_softc *
+       struct cmv_e1 cmv;
+       int ret;
+-      uea_enters(INS_TO_USBDEV(sc));
+       uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Address : %c%c%c%c, "
+                       "offset : 0x%04x, data : 0x%08x\n",
+                       E1_FUNCTION_TYPE(function),
+@@ -1163,9 +1152,8 @@ static int uea_cmv_e1(struct uea_softc *
+                                                       sizeof(cmv), &cmv);
+       if (ret < 0)
+               return ret;
+-      ret = wait_cmv_ack(sc);
+-      uea_leaves(INS_TO_USBDEV(sc));
+-      return ret;
++
++      return wait_cmv_ack(sc);
+ }
+ static int uea_cmv_e4(struct uea_softc *sc,
+@@ -1174,7 +1162,6 @@ static int uea_cmv_e4(struct uea_softc *
+       struct cmv_e4 cmv;
+       int ret;
+-      uea_enters(INS_TO_USBDEV(sc));
+       memset(&cmv, 0, sizeof(cmv));
+       uea_vdbg(INS_TO_USBDEV(sc), "Function : %d-%d, Group : 0x%04x, "
+@@ -1198,9 +1185,8 @@ static int uea_cmv_e4(struct uea_softc *
+                                                       sizeof(cmv), &cmv);
+       if (ret < 0)
+               return ret;
+-      ret = wait_cmv_ack(sc);
+-      uea_leaves(INS_TO_USBDEV(sc));
+-      return ret;
++
++      return wait_cmv_ack(sc);
+ }
+ static inline int uea_read_cmv_e1(struct uea_softc *sc,
+@@ -1294,7 +1280,6 @@ static int uea_stat_e1(struct uea_softc
+       u32 data;
+       int ret;
+-      uea_enters(INS_TO_USBDEV(sc));
+       data = sc->stats.phy.state;
+       ret = uea_read_cmv_e1(sc, E1_SA_STAT, 0, &sc->stats.phy.state);
+@@ -1437,7 +1422,6 @@ static int uea_stat_e4(struct uea_softc
+       u32 tmp_arr[2];
+       int ret;
+-      uea_enters(INS_TO_USBDEV(sc));
+       data = sc->stats.phy.state;
+       /* XXX only need to be done before operationnal... */
+@@ -1804,7 +1788,6 @@ static int uea_start_reset(struct uea_so
+       u16 zero = 0;   /* ;-) */
+       int ret;
+-      uea_enters(INS_TO_USBDEV(sc));
+       uea_info(INS_TO_USBDEV(sc), "(re)booting started\n");
+       /* mask interrupt */
+@@ -1872,7 +1855,6 @@ static int uea_start_reset(struct uea_so
+               return ret;
+       sc->reset = 0;
+-      uea_leaves(INS_TO_USBDEV(sc));
+       return ret;
+ }
+@@ -1888,7 +1870,6 @@ static int uea_kthread(void *data)
+       int ret = -EAGAIN;
+       set_freezable();
+-      uea_enters(INS_TO_USBDEV(sc));
+       while (!kthread_should_stop()) {
+               if (ret < 0 || sc->reset)
+                       ret = uea_start_reset(sc);
+@@ -1897,7 +1878,7 @@ static int uea_kthread(void *data)
+               if (ret != -EAGAIN)
+                       uea_wait(sc, 0, msecs_to_jiffies(1000));
+       }
+-      uea_leaves(INS_TO_USBDEV(sc));
++
+       return ret;
+ }
+@@ -1910,8 +1891,6 @@ static int load_XILINX_firmware(struct u
+       u8 value;
+       char *fw_name = FPGA930_FIRMWARE;
+-      uea_enters(INS_TO_USBDEV(sc));
+-
+       ret = request_firmware(&fw_entry, fw_name, &sc->usb_dev->dev);
+       if (ret) {
+               uea_err(INS_TO_USBDEV(sc), "firmware %s is not available\n",
+@@ -1955,7 +1934,6 @@ static int load_XILINX_firmware(struct u
+ err1:
+       release_firmware(fw_entry);
+ err0:
+-      uea_leaves(INS_TO_USBDEV(sc));
+       return ret;
+ }
+@@ -1965,7 +1943,6 @@ static void uea_dispatch_cmv_e1(struct u
+       struct cmv_dsc_e1 *dsc = &sc->cmv_dsc.e1;
+       struct cmv_e1 *cmv = &intr->u.e1.s2.cmv;
+-      uea_enters(INS_TO_USBDEV(sc));
+       if (le16_to_cpu(cmv->wPreamble) != E1_PREAMBLE)
+               goto bad1;
+@@ -1989,7 +1966,6 @@ static void uea_dispatch_cmv_e1(struct u
+       if (cmv->bFunction == E1_MAKEFUNCTION(E1_ADSLDIRECTIVE,
+                                                       E1_MODEMREADY)) {
+               wake_up_cmv_ack(sc);
+-              uea_leaves(INS_TO_USBDEV(sc));
+               return;
+       }
+@@ -2003,7 +1979,6 @@ static void uea_dispatch_cmv_e1(struct u
+       sc->data = sc->data << 16 | sc->data >> 16;
+       wake_up_cmv_ack(sc);
+-      uea_leaves(INS_TO_USBDEV(sc));
+       return;
+ bad2:
+@@ -2011,14 +1986,12 @@ bad2:
+                       "Function : %d, Subfunction : %d\n",
+                       E1_FUNCTION_TYPE(cmv->bFunction),
+                       E1_FUNCTION_SUBTYPE(cmv->bFunction));
+-      uea_leaves(INS_TO_USBDEV(sc));
+       return;
+ bad1:
+       uea_err(INS_TO_USBDEV(sc), "invalid cmv received, "
+                       "wPreamble %d, bDirection %d\n",
+                       le16_to_cpu(cmv->wPreamble), cmv->bDirection);
+-      uea_leaves(INS_TO_USBDEV(sc));
+ }
+ /* The modem send us an ack. First with check if it right */
+@@ -2027,7 +2000,6 @@ static void uea_dispatch_cmv_e4(struct u
+       struct cmv_dsc_e4 *dsc = &sc->cmv_dsc.e4;
+       struct cmv_e4 *cmv = &intr->u.e4.s2.cmv;
+-      uea_enters(INS_TO_USBDEV(sc));
+       uea_dbg(INS_TO_USBDEV(sc), "cmv %x %x %x %x %x %x\n",
+               be16_to_cpu(cmv->wGroup), be16_to_cpu(cmv->wFunction),
+               be16_to_cpu(cmv->wOffset), be16_to_cpu(cmv->wAddress),
+@@ -2039,7 +2011,6 @@ static void uea_dispatch_cmv_e4(struct u
+       if (be16_to_cpu(cmv->wFunction) == E4_MAKEFUNCTION(E4_ADSLDIRECTIVE,
+                                               E4_MODEMREADY, 1)) {
+               wake_up_cmv_ack(sc);
+-              uea_leaves(INS_TO_USBDEV(sc));
+               return;
+       }
+@@ -2052,7 +2023,6 @@ static void uea_dispatch_cmv_e4(struct u
+       sc->data = be32_to_cpu(cmv->dwData[0]);
+       sc->data1 = be32_to_cpu(cmv->dwData[1]);
+       wake_up_cmv_ack(sc);
+-      uea_leaves(INS_TO_USBDEV(sc));
+       return;
+ bad2:
+@@ -2060,7 +2030,6 @@ bad2:
+                       "Function : %d, Subfunction : %d\n",
+                       E4_FUNCTION_TYPE(cmv->wFunction),
+                       E4_FUNCTION_SUBTYPE(cmv->wFunction));
+-      uea_leaves(INS_TO_USBDEV(sc));
+       return;
+ }
+@@ -2088,8 +2057,6 @@ static void uea_intr(struct urb *urb)
+       struct intr_pkt *intr = urb->transfer_buffer;
+       int status = urb->status;
+-      uea_enters(INS_TO_USBDEV(sc));
+-
+       if (unlikely(status < 0)) {
+               uea_err(INS_TO_USBDEV(sc), "uea_intr() failed with %d\n",
+                      status);
+@@ -2129,8 +2096,6 @@ static int uea_boot(struct uea_softc *sc
+       int ret = -ENOMEM;
+       int size;
+-      uea_enters(INS_TO_USBDEV(sc));
+-
+       if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
+               size = E4_INTR_PKT_SIZE;
+               sc->dispatch_cmv = uea_dispatch_cmv_e4;
+@@ -2187,7 +2152,6 @@ static int uea_boot(struct uea_softc *sc
+               goto err2;
+       }
+-      uea_leaves(INS_TO_USBDEV(sc));
+       return 0;
+ err2:
+@@ -2197,7 +2161,6 @@ err1:
+       sc->urb_int = NULL;
+       kfree(intr);
+ err0:
+-      uea_leaves(INS_TO_USBDEV(sc));
+       return ret;
+ }
+@@ -2207,7 +2170,7 @@ err0:
+ static void uea_stop(struct uea_softc *sc)
+ {
+       int ret;
+-      uea_enters(INS_TO_USBDEV(sc));
++
+       ret = kthread_stop(sc->kthread);
+       uea_dbg(INS_TO_USBDEV(sc), "kthread finish with status %d\n", ret);
+@@ -2221,7 +2184,6 @@ static void uea_stop(struct uea_softc *s
+       flush_work(&sc->task);
+       release_firmware(sc->dsp_firm);
+-      uea_leaves(INS_TO_USBDEV(sc));
+ }
+ /* syfs interface */
+@@ -2494,8 +2456,6 @@ static int uea_bind(struct usbatm_data *
+       int ret, ifnum = intf->altsetting->desc.bInterfaceNumber;
+       unsigned int alt;
+-      uea_enters(usb);
+-
+       /* interface 0 is for firmware/monitoring */
+       if (ifnum != UEA_INTR_IFACE_NO)
+               return -ENODEV;
+@@ -2588,7 +2548,6 @@ static int uea_probe(struct usb_interfac
+       struct usb_device *usb = interface_to_usbdev(intf);
+       int ret;
+-      uea_enters(usb);
+       uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n",
+               le16_to_cpu(usb->descriptor.idVendor),
+               le16_to_cpu(usb->descriptor.idProduct),
+@@ -2619,7 +2578,6 @@ static void uea_disconnect(struct usb_in
+ {
+       struct usb_device *usb = interface_to_usbdev(intf);
+       int ifnum = intf->altsetting->desc.bInterfaceNumber;
+-      uea_enters(usb);
+       /* ADI930 has 2 interfaces and eagle 3 interfaces.
+        * Pre-firmware device has one interface
+@@ -2630,8 +2588,6 @@ static void uea_disconnect(struct usb_in
+               mutex_unlock(&uea_mutex);
+               uea_info(usb, "ADSL device removed\n");
+       }
+-
+-      uea_leaves(usb);
+ }
+ /*
diff --git a/queue-6.18/usb-atm-ueagle-atm-use-dev_dbg-for-device-found-message.patch b/queue-6.18/usb-atm-ueagle-atm-use-dev_dbg-for-device-found-message.patch
new file mode 100644 (file)
index 0000000..48c4aab
--- /dev/null
@@ -0,0 +1,47 @@
+From stable+bounces-277437-greg=kroah.com@vger.kernel.org Sun Jul 19 14:43:58 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 08:43:46 -0400
+Subject: usb: atm: ueagle-atm: use dev_dbg() for 'device found' message
+To: stable@vger.kernel.org
+Cc: Mauricio Faria de Oliveira <mfo@igalia.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Stanislaw Gruszka <stf_xl@wp.pl>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260719124348.3646001-3-sashal@kernel.org>
+
+From: Mauricio Faria de Oliveira <mfo@igalia.com>
+
+[ Upstream commit 05c5075c3115010f97434c53d353d9a0d4b0e64e ]
+
+Convert dev_info() to dev_dbg().
+
+Per 'Documentation/process/coding-style.rst':
+
+  13) Printing kernel messages
+
+  ...  When drivers are working properly they are quiet,
+  so prefer to use dev_dbg/pr_debug unless something is wrong.
+
+While in there, correct the verb form and add 'with' for clarity.
+
+Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Link: https://lore.kernel.org/all/2026051628-squatter-stature-c0e0@gregkh/
+Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
+Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
+Link: https://patch.msgid.link/20260520-ueagle-atm-cleanup-v1-1-010c8bc7b214@igalia.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: e2674dfbed8a ("usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/atm/ueagle-atm.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/atm/ueagle-atm.c
++++ b/drivers/usb/atm/ueagle-atm.c
+@@ -2589,7 +2589,7 @@ static int uea_probe(struct usb_interfac
+       int ret;
+       uea_enters(usb);
+-      uea_info(usb, "ADSL device founded vid (%#X) pid (%#X) Rev (%#X): %s\n",
++      uea_dbg(usb, "ADSL device found with vid (%#X) pid (%#X) Rev (%#X): %s\n",
+               le16_to_cpu(usb->descriptor.idVendor),
+               le16_to_cpu(usb->descriptor.idProduct),
+               le16_to_cpu(usb->descriptor.bcdDevice),
diff --git a/queue-6.18/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch b/queue-6.18/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch
new file mode 100644 (file)
index 0000000..8f44c7c
--- /dev/null
@@ -0,0 +1,175 @@
+From stable+bounces-277439-greg=kroah.com@vger.kernel.org Sun Jul 19 14:44:00 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 08:43:48 -0400
+Subject: usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect()
+To: stable@vger.kernel.org
+Cc: Mauricio Faria de Oliveira <mfo@igalia.com>, stable <stable@kernel.org>, syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com, syzbot+306212936b13e520679d@syzkaller.appspotmail.com, syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com, Andrey Tsygunka <aitsygunka@yandex.ru>, Stanislaw Gruszka <stf_xl@wp.pl>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260719124348.3646001-5-sashal@kernel.org>
+
+From: Mauricio Faria de Oliveira <mfo@igalia.com>
+
+[ Upstream commit e2674dfbed8a30d57e2bc872c4bfa6c3eec918bf ]
+
+ueagle-atm uses the asynchronous request_firmware_nowait() in .probe(),
+but does not wait for its completion, not even in .disconnect(); so, if the
+device is unplugged meanwhile, its teardown runs concurrently with that.
+
+Even though this inconsistency is worth addressing on its own, it has also
+triggered several bug reports in syzbot over the years (some auto-closed)
+where the firmware sysfs fallback mechanism (CONFIG_FW_LOADER_USER_HELPER)
+creates a firmware subdirectory in the device directory during its removal,
+which might hit unexpected conditions in kernfs, apparently, depending at
+which point the add and remove operations raced. (See links.)
+
+The pattern is:
+
+usb ?-?: Direct firmware load for ueagle-atm/eagle?.fw failed with error -2
+usb ?-?: Falling back to sysfs fallback for: ueagle-atm/eagle?.fw
+<ERROR>
+Call trace:
+ ...
+ kernfs_create_dir_ns
+ sysfs_create_dir_ns
+ create_dir
+ kobject_add_internal
+ kobject_add_varg
+ kobject_add
+ class_dir_create_and_add
+ get_device_parent
+ device_add
+ fw_load_sysfs_fallback
+ fw_load_from_user_helper
+ firmware_fallback_sysfs
+ _request_firmware
+ request_firmware_work_func
+ ...
+
+(Some variations are observed, after fw_load_sysfs_fallback(), e.g., [1].)
+
+While the kernfs side is being looked at, the ueagle-atm side can be fixed
+by waiting for the pre-firmware load in the .disconnect() handler.
+
+This change has a similar approach to previous work by Andrey Tsygunka [2]
+(wait_for_completion() in .disconnect()), but it is relatively different in
+design/implementation; using the Originally-by tag for credit assignment.
+
+This has been tested with:
+- synthetic reproducer to check the error path;
+- USB gadget (virtual device) to check the firmware upload path;
+- QEMU device emulator to check the device ID re-enumeration path;
+(The latter two were written by Claude; no other code/text in this commit.)
+
+Links (year first reported):
+ 2025 https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d
+ 2025 https://syzbot.org/bug?extid=9af8471255ac36e34fd4
+ 2024 https://syzbot.org/bug?extid=306212936b13e520679d
+ 2023 https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2
+ 2022 https://syzbot.org/bug?extid=782984d6f1701b526edb
+ 2021 https://syzbot.org/bug?id=f3f221579f4ef7e9691281f3c6f56c05f83e8490
+ 2021 https://syzbot.org/bug?id=84d86f0d71394829df6fc53daf6642c045983881
+ 2021 https://syzbot.org/bug?id=3302dc1c0e2b9c94f2e8edb404eabc9267bc6f90
+
+[1] https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2
+[2] https://lore.kernel.org/lkml/20250410093146.3776801-2-aitsygunka@yandex.ru/
+
+Cc: stable <stable@kernel.org>
+Reported-by: syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com
+Closes: https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d
+Reported-by: syzbot+306212936b13e520679d@syzkaller.appspotmail.com
+Closes: https://syzbot.org/bug?extid=306212936b13e520679d
+Reported-by: syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2
+Originally-by: Andrey Tsygunka <aitsygunka@yandex.ru>
+Fixes: b72458a80c75 ("[PATCH] USB: Eagle and ADI 930 usb adsl modem driver")
+Assisted-by: Claude:claude-opus-4.7 # usb gadget & qemu device for testing
+Signed-off-by: Mauricio Faria de Oliveira <mfo@igalia.com>
+Acked-by: Stanislaw Gruszka <stf_xl@wp.pl>
+Link: https://patch.msgid.link/20260526-ueagle-atm_req-fw-sync-v3-1-93c01961daaf@igalia.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/atm/ueagle-atm.c |   36 +++++++++++++++++++++++++++++++-----
+ 1 file changed, 31 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/atm/ueagle-atm.c
++++ b/drivers/usb/atm/ueagle-atm.c
+@@ -593,7 +593,9 @@ static int uea_send_modem_cmd(struct usb
+ static void uea_upload_pre_firmware(const struct firmware *fw_entry,
+                                                               void *context)
+ {
+-      struct usb_device *usb = context;
++      struct usb_interface *intf = context;
++      struct usb_device *usb = interface_to_usbdev(intf);
++      struct completion *fw_done = usb_get_intfdata(intf);
+       const u8 *pfw;
+       u8 value;
+       u32 crc = 0;
+@@ -662,15 +664,17 @@ err_fw_corrupted:
+       uea_err(usb, "firmware is corrupted\n");
+ err:
+       release_firmware(fw_entry);
++      complete(fw_done);
+ }
+ /*
+  * uea_load_firmware - Load usb firmware for pre-firmware devices.
+  */
+-static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
++static int uea_load_firmware(struct usb_interface *intf, unsigned int ver)
+ {
+       int ret;
+       char *fw_name = EAGLE_FIRMWARE;
++      struct usb_device *usb = interface_to_usbdev(intf);
+       uea_info(usb, "pre-firmware device, uploading firmware\n");
+@@ -693,7 +697,7 @@ static int uea_load_firmware(struct usb_
+       }
+       ret = request_firmware_nowait(THIS_MODULE, 1, fw_name, &usb->dev,
+-                                      GFP_KERNEL, usb,
++                                      GFP_KERNEL, intf,
+                                       uea_upload_pre_firmware);
+       if (ret)
+               uea_err(usb, "firmware %s is not available\n", fw_name);
+@@ -2556,8 +2560,23 @@ static int uea_probe(struct usb_interfac
+       usb_reset_device(usb);
+-      if (UEA_IS_PREFIRM(id))
+-              return uea_load_firmware(usb, UEA_CHIP_VERSION(id));
++      if (UEA_IS_PREFIRM(id)) {
++              struct completion *fw_done;
++
++              /* Wait for the firmware load to be done, in .disconnect() */
++              fw_done = kzalloc_obj(*fw_done);
++              if (!fw_done)
++                      return -ENOMEM;
++
++              init_completion(fw_done);
++              usb_set_intfdata(intf, fw_done);
++
++              ret = uea_load_firmware(intf, UEA_CHIP_VERSION(id));
++              if (ret)
++                      kfree(fw_done);
++
++              return ret;
++      }
+       ret = usbatm_usb_probe(intf, id, &uea_usbatm_driver);
+       if (ret == 0) {
+@@ -2587,6 +2606,13 @@ static void uea_disconnect(struct usb_in
+               usbatm_usb_disconnect(intf);
+               mutex_unlock(&uea_mutex);
+               uea_info(usb, "ADSL device removed\n");
++      } else if (usb->config->desc.bNumInterfaces == 1) {
++              struct completion *fw_done = usb_get_intfdata(intf);
++
++              uea_dbg(usb, "pre-firmware device, waiting firmware upload\n");
++              wait_for_completion(fw_done);
++              uea_dbg(usb, "pre-firmware device, finished waiting\n");
++              kfree(fw_done);
+       }
+ }
diff --git a/queue-6.18/usb-dwc3-fix-dwc3_readl-and-dwc3_writel-calls-in-dwc3_ulpi_setup.patch b/queue-6.18/usb-dwc3-fix-dwc3_readl-and-dwc3_writel-calls-in-dwc3_ulpi_setup.patch
new file mode 100644 (file)
index 0000000..651b993
--- /dev/null
@@ -0,0 +1,51 @@
+From stable+bounces-277386-greg=kroah.com@vger.kernel.org Sat Jul 18 23:51:02 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 17:50:15 -0400
+Subject: usb: dwc3: fix dwc3_readl() and dwc3_writel() calls in dwc3_ulpi_setup()
+To: stable@vger.kernel.org
+Cc: Ben Dooks <ben.dooks@codethink.co.uk>, stable <stable@kernel.org>, Thinh Nguyen <Thinh.Nguyen@synopsys.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718215015.3447229-2-sashal@kernel.org>
+
+From: Ben Dooks <ben.dooks@codethink.co.uk>
+
+[ Upstream commit e0f844d9d74200d311c6438a0f04270834ba5365 ]
+
+The dwc3_ulpi_setup() calls the register read and write calls with
+dwc3->regs when both these calls take the dwc3 structure directly.
+
+Chnage these two calls to fix the following sparse warning, and
+possibly a nasty bug in the dwc3_ulpi_setup() code:
+
+drivers/usb/dwc3/core.c:796:45: warning: incorrect type in argument 1 (different address spaces)
+drivers/usb/dwc3/core.c:796:45:    expected struct dwc3 *dwc
+drivers/usb/dwc3/core.c:796:45:    got void [noderef] __iomem *regs
+drivers/usb/dwc3/core.c:798:40: warning: incorrect type in argument 1 (different address spaces)
+drivers/usb/dwc3/core.c:798:40:    expected struct dwc3 *dwc
+drivers/usb/dwc3/core.c:798:40:    got void [noderef] __iomem *regs
+
+Cc: stable <stable@kernel.org>
+Fixes: 9accc68b1cf0 ("usb: dwc3: Add dwc pointer to dwc3_readl/writel")
+Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
+Link: https://patch.msgid.link/20260703162033.2847599-1-ben.dooks@codethink.co.uk
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/dwc3/core.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/dwc3/core.c
++++ b/drivers/usb/dwc3/core.c
+@@ -792,9 +792,9 @@ static void dwc3_ulpi_setup(struct dwc3
+       if (dwc->enable_usb2_transceiver_delay) {
+               for (index = 0; index < dwc->num_usb2_ports; index++) {
+-                      reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
++                      reg = dwc3_readl(dwc, DWC3_GUSB2PHYCFG(index));
+                       reg |= DWC3_GUSB2PHYCFG_XCVRDLY;
+-                      dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
++                      dwc3_writel(dwc, DWC3_GUSB2PHYCFG(index), reg);
+               }
+       }
+ }
diff --git a/queue-6.18/usb-dwc3-support-usb3340x-ulpi-phy-high-speed-negotiation.patch b/queue-6.18/usb-dwc3-support-usb3340x-ulpi-phy-high-speed-negotiation.patch
new file mode 100644 (file)
index 0000000..3d6de20
--- /dev/null
@@ -0,0 +1,151 @@
+From stable+bounces-277385-greg=kroah.com@vger.kernel.org Sat Jul 18 23:51:08 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 17:50:14 -0400
+Subject: usb: dwc3: Support USB3340x ULPI PHY high-speed negotiation.
+To: stable@vger.kernel.org
+Cc: Ingo Rohloff <ingo.rohloff@lauterbach.com>, Thinh Nguyen <Thinh.Nguyen@synopsys.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718215015.3447229-1-sashal@kernel.org>
+
+From: Ingo Rohloff <ingo.rohloff@lauterbach.com>
+
+[ Upstream commit a28de63356575612954d4e5d5f48a2488f50e16d ]
+
+The Microchip USB3340x ULPI PHY requires a delay when switching to the
+high-speed transmitter. See:
+    http://ww1.microchip.com/downloads/en/DeviceDoc/80000645A.pdf
+    Module 2 "Device Enumeration Failure with Link IP Systems"
+
+For details on the behavior and fix, refer to the AMD (formerly Xilinx)
+forum post: "USB stuck in full speed mode with USB3340 ULPI PHY, ZynqMP."
+
+This patch uses the USB PHY Vendor-ID and Product-ID to detect the
+USB3340 PHY and then applies the necessary fix if this PHY is found.
+
+Signed-off-by: Ingo Rohloff <ingo.rohloff@lauterbach.com>
+Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://patch.msgid.link/20260305121452.54082-2-ingo.rohloff@lauterbach.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: e0f844d9d742 ("usb: dwc3: fix dwc3_readl() and dwc3_writel() calls in dwc3_ulpi_setup()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/dwc3/core.c |   20 ++++++++++++++++++++
+ drivers/usb/dwc3/core.h |    4 ++++
+ drivers/usb/dwc3/ulpi.c |   25 +++++++++++++++++++++++++
+ 3 files changed, 49 insertions(+)
+
+--- a/drivers/usb/dwc3/core.c
++++ b/drivers/usb/dwc3/core.c
+@@ -781,6 +781,24 @@ static int dwc3_hs_phy_setup(struct dwc3
+       return 0;
+ }
++static void dwc3_ulpi_setup(struct dwc3 *dwc)
++{
++      int index;
++      u32 reg;
++
++      /* Don't do anything if there is no ULPI PHY */
++      if (!dwc->ulpi)
++              return;
++
++      if (dwc->enable_usb2_transceiver_delay) {
++              for (index = 0; index < dwc->num_usb2_ports; index++) {
++                      reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));
++                      reg |= DWC3_GUSB2PHYCFG_XCVRDLY;
++                      dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg);
++              }
++      }
++}
++
+ /**
+  * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
+  * @dwc: Pointer to our controller context structure
+@@ -1355,6 +1373,8 @@ static int dwc3_core_init(struct dwc3 *d
+               dwc->ulpi_ready = true;
+       }
++      dwc3_ulpi_setup(dwc);
++
+       if (!dwc->phys_ready) {
+               ret = dwc3_core_get_phy(dwc);
+               if (ret)
+--- a/drivers/usb/dwc3/core.h
++++ b/drivers/usb/dwc3/core.h
+@@ -302,6 +302,7 @@
+ #define DWC3_GUSB2PHYCFG_SUSPHY               BIT(6)
+ #define DWC3_GUSB2PHYCFG_ULPI_UTMI    BIT(4)
+ #define DWC3_GUSB2PHYCFG_ENBLSLPM     BIT(8)
++#define DWC3_GUSB2PHYCFG_XCVRDLY      BIT(9)
+ #define DWC3_GUSB2PHYCFG_PHYIF(n)     (n << 3)
+ #define DWC3_GUSB2PHYCFG_PHYIF_MASK   DWC3_GUSB2PHYCFG_PHYIF(1)
+ #define DWC3_GUSB2PHYCFG_USBTRDTIM(n) (n << 10)
+@@ -1161,6 +1162,8 @@ struct dwc3_glue_ops {
+  *    3       - Reserved
+  * @dis_metastability_quirk: set to disable metastability quirk.
+  * @dis_split_quirk: set to disable split boundary.
++ * @enable_usb2_transceiver_delay: Set to insert a delay before the
++ *                    assertion of the TxValid signal during a HS Chirp.
+  * @sys_wakeup: set if the device may do system wakeup.
+  * @wakeup_configured: set if the device is configured for remote wakeup.
+  * @suspended: set to track suspend event due to U3/L2.
+@@ -1403,6 +1406,7 @@ struct dwc3 {
+       unsigned                dis_metastability_quirk:1;
+       unsigned                dis_split_quirk:1;
++      unsigned                enable_usb2_transceiver_delay:1;
+       unsigned                async_callbacks:1;
+       unsigned                sys_wakeup:1;
+       unsigned                wakeup_configured:1;
+--- a/drivers/usb/dwc3/ulpi.c
++++ b/drivers/usb/dwc3/ulpi.c
+@@ -10,10 +10,13 @@
+ #include <linux/delay.h>
+ #include <linux/time64.h>
+ #include <linux/ulpi/regs.h>
++#include <linux/ulpi/driver.h>
+ #include "core.h"
+ #include "io.h"
++#define USB_VENDOR_MICROCHIP 0x0424
++
+ #define DWC3_ULPI_ADDR(a) \
+               ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \
+               DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
+@@ -83,6 +86,26 @@ static const struct ulpi_ops dwc3_ulpi_o
+       .write = dwc3_ulpi_write,
+ };
++static void dwc3_ulpi_detect_config(struct dwc3 *dwc)
++{
++      struct ulpi *ulpi = dwc->ulpi;
++
++      switch (ulpi->id.vendor) {
++      case USB_VENDOR_MICROCHIP:
++              switch (ulpi->id.product) {
++              case 0x0009:
++                      /* Microchip USB3340 ULPI PHY */
++                      dwc->enable_usb2_transceiver_delay = true;
++                      break;
++              default:
++                      break;
++              }
++              break;
++      default:
++              break;
++      }
++}
++
+ int dwc3_ulpi_init(struct dwc3 *dwc)
+ {
+       /* Register the interface */
+@@ -92,6 +115,8 @@ int dwc3_ulpi_init(struct dwc3 *dwc)
+               return PTR_ERR(dwc->ulpi);
+       }
++      dwc3_ulpi_detect_config(dwc);
++
+       return 0;
+ }
diff --git a/queue-6.18/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch b/queue-6.18/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch
new file mode 100644 (file)
index 0000000..c1e6e71
--- /dev/null
@@ -0,0 +1,101 @@
+From stable+bounces-277546-greg=kroah.com@vger.kernel.org Mon Jul 20 04:36:24 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 19 Jul 2026 22:25:51 -0400
+Subject: usb: gadget: f_fs: initialize reset_work at allocation time
+To: stable@vger.kernel.org
+Cc: "Tyler Baker" <tyler.baker@oss.qualcomm.com>, stable <stable@kernel.org>, "Loic Poulain" <loic.poulain@oss.qualcomm.com>, "Dmitry Baryshkov" <dmitry.baryshkov@oss.qualcomm.com>, "Srinivas Kandagatla" <srinivas.kandagatla@oss.qualcomm.com>, "Peter Chen" <peter.chen@kernel.org>, "Michał Nazarewicz" <mina86@mina86.com>, "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260720023553.2928009-6-sashal@kernel.org>
+
+From: Tyler Baker <tyler.baker@oss.qualcomm.com>
+
+[ Upstream commit 3137b243c93982fe3460335e12f9247739766e10 ]
+
+ffs_fs_kill_sb() unconditionally calls cancel_work_sync() on
+ffs->reset_work when a functionfs instance is unmounted:
+
+       ffs_data_reset(ffs);
+       cancel_work_sync(&ffs->reset_work);
+
+However ffs->reset_work is only ever initialized via INIT_WORK() in
+ffs_func_set_alt() and ffs_func_disable(), and only on the
+FFS_DEACTIVATED path. That state is reached solely by ffs_data_closed()
+when the instance is mounted with the "no_disconnect" option, so for the
+common case (no "no_disconnect", or mounted and unmounted without ever
+being deactivated) reset_work is never initialized.
+
+ffs_data_new() allocates the ffs_data with kzalloc_obj() and does not
+initialize reset_work, and ffs_data_reset()/ffs_data_clear() do not touch
+it either, so reset_work.func is left NULL. cancel_work_sync() on such a
+work then trips the WARN_ON(!work->func) guard in __flush_work():
+
+  WARNING: kernel/workqueue.c:4301 at __flush_work+0x330/0x360, CPU#3: umount
+  Call trace:
+   __flush_work
+   cancel_work_sync
+   ffs_fs_kill_sb [usb_f_fs]
+   deactivate_locked_super
+   deactivate_super
+   cleanup_mnt
+   __cleanup_mnt
+   task_work_run
+   exit_to_user_mode_loop
+   el0_svc
+
+On older kernels cancel_work_sync() on a zero-initialized work struct was
+a silent no-op, which hid the missing initialization.
+
+Initialize reset_work once in ffs_data_new() so it is always valid for
+the lifetime of the ffs_data, and drop the now-redundant INIT_WORK()
+calls from the two deactivation paths.
+
+Fixes: 18d6b32fca38 ("usb: gadget: f_fs: add "no_disconnect" mode")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Tyler Baker <tyler.baker@oss.qualcomm.com>
+Cc: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Cc: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+Cc: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
+Tested-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
+Reviewed-by: Peter Chen <peter.chen@kernel.org>
+Acked-by: Michał Nazarewicz <mina86@mina86.com>
+Link: https://patch.msgid.link/20260609193635.2284430-1-tyler.baker@oss.qualcomm.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_fs.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -289,6 +289,7 @@ static int ffs_acquire_dev(const char *d
+ static void ffs_release_dev(struct ffs_dev *ffs_dev);
+ static int ffs_ready(struct ffs_data *ffs);
+ static void ffs_closed(struct ffs_data *ffs);
++static void ffs_reset_work(struct work_struct *work);
+ /* Misc helper functions ****************************************************/
+@@ -2206,6 +2207,7 @@ static struct ffs_data *ffs_data_new(con
+       init_waitqueue_head(&ffs->ev.waitq);
+       init_waitqueue_head(&ffs->wait);
+       init_completion(&ffs->ep0req_completion);
++      INIT_WORK(&ffs->reset_work, ffs_reset_work);
+       /* XXX REVISIT need to update it in some places, or do we? */
+       ffs->ev.can_stall = 1;
+@@ -3753,7 +3755,6 @@ static int ffs_func_set_alt(struct usb_f
+       if (ffs->state == FFS_DEACTIVATED) {
+               ffs->state = FFS_CLOSING;
+               spin_unlock_irqrestore(&ffs->eps_lock, flags);
+-              INIT_WORK(&ffs->reset_work, ffs_reset_work);
+               schedule_work(&ffs->reset_work);
+               return -ENODEV;
+       }
+@@ -3784,7 +3785,6 @@ static void ffs_func_disable(struct usb_
+       if (ffs->state == FFS_DEACTIVATED) {
+               ffs->state = FFS_CLOSING;
+               spin_unlock_irqrestore(&ffs->eps_lock, flags);
+-              INIT_WORK(&ffs->reset_work, ffs_reset_work);
+               schedule_work(&ffs->reset_work);
+               return;
+       }
diff --git a/queue-6.18/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch b/queue-6.18/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch
new file mode 100644 (file)
index 0000000..c61e9c0
--- /dev/null
@@ -0,0 +1,54 @@
+From sashal@kernel.org Mon Jul 20 12:54:57 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 20 Jul 2026 06:54:48 -0400
+Subject: usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile
+To: stable@vger.kernel.org
+Cc: Neill Kapron <nkapron@google.com>, stable <stable@kernel.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260720105452.216179-3-sashal@kernel.org>
+
+From: Neill Kapron <nkapron@google.com>
+
+[ Upstream commit 8bdcf96eb135aebacac319667f87db034fb38406 ]
+
+Currently, ffs_epfile_release unconditionally frees the endpoint's
+read_buffer when a file descriptor is closed. If userspace explicitly
+opens the endpoint multiple times and closes one, the read_buffer is
+destroyed. This can lead to silent data loss if other file descriptors
+are still actively reading from the endpoint.
+
+By tying the lifetime of the read_buffer to the ffs_epfile structure itself
+(which is destroyed when the functionfs instance is torn down in
+ffs_epfiles_destroy), we eliminate the brittle dependency on open/release
+calls while correctly matching the conceptual lifetime of unread data on
+the hardware endpoint.
+
+Fixes: 9353afbbfa7b ("usb: gadget: f_fs: buffer data from ‘oversized’ OUT requests")
+Cc: stable <stable@kernel.org>
+Assisted-by: Antigravity:gemini-3.1-pro
+Signed-off-by: Neill Kapron <nkapron@google.com>
+Link: https://patch.msgid.link/20260619040609.4010746-3-nkapron@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_fs.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -1353,7 +1353,6 @@ ffs_epfile_release(struct inode *inode,
+       mutex_unlock(&epfile->dmabufs_mutex);
+-      __ffs_epfile_read_buffer_free(epfile);
+       ffs_data_closed(epfile->ffs);
+       return 0;
+@@ -2370,6 +2369,7 @@ static void ffs_epfiles_destroy(struct s
+       for (; count; --count, ++epfile) {
+               BUG_ON(mutex_is_locked(&epfile->mutex));
++              __ffs_epfile_read_buffer_free(epfile);
+               simple_remove_by_name(root, epfile->name, NULL);
+       }
diff --git a/queue-6.18/vfio-mlx5-fix-racy-bitfields-and-tighten-struct-layout.patch b/queue-6.18/vfio-mlx5-fix-racy-bitfields-and-tighten-struct-layout.patch
new file mode 100644 (file)
index 0000000..9a3744f
--- /dev/null
@@ -0,0 +1,95 @@
+From stable+bounces-274246-greg=kroah.com@vger.kernel.org Tue Jul 14 15:32:39 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 14 Jul 2026 09:27:08 -0400
+Subject: vfio/mlx5: Fix racy bitfields and tighten struct layout
+To: stable@vger.kernel.org
+Cc: Alex Williamson <alex.williamson@nvidia.com>, Yishai Hadas <yishaih@nvidia.com>, Kevin Tian <kevin.tian@intel.com>, Alex Williamson <alex@shazbot.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260714132708.2663976-1-sashal@kernel.org>
+
+From: Alex Williamson <alex.williamson@nvidia.com>
+
+[ Upstream commit f2365a63b02ddea32e7db78b742c2503ec7b81f1 ]
+
+Bitfield operations are not atomic, they use a read-modify-write
+pattern, therefore we should be careful not to pack bitfields that
+can be concurrently updated into the same storage unit.
+
+This split takes a binary approach: flags that are only modified
+pre/post open/close remain bitfields, flags modified from user
+action, including actions that reach across to another device (ex.
+reset) use dedicated storage units.
+
+Note mlx5_vhca_page_tracker.status is relocated to fill the alignment
+hole this split exposes.
+
+Bitfield justifications:
+
+  migrate_cap: written only in mlx5vf_cmd_set_migratable() at probe
+  chunk_mode: written only in mlx5vf_cmd_set_migratable() at probe
+  mig_state_cap: written only in mlx5vf_cmd_set_migratable() at probe
+
+Dedicated storage units:
+
+  mdev_detach: written in the VF attach/detach event notifier
+               mlx5fv_vf_event() at runtime
+  log_active: written in mlx5vf_start_page_tracker()/
+              mlx5vf_stop_page_tracker() during runtime dirty tracking
+  deferred_reset: written in mlx5vf_state_mutex_unlock()/
+                  mlx5vf_pci_aer_reset_done() during runtime reset handling
+  is_err: set by tracker error handling and dirty-log polling at runtime
+  object_changed: set by tracker event handling and cleared by dirty-log
+                  polling at runtime
+
+Fixes: 61a2f1460fd0 ("vfio/mlx5: Manage the VF attach/detach callback from the PF")
+Fixes: 79c3cf279926 ("vfio/mlx5: Init QP based resources for dirty tracking")
+Fixes: f886473071d6 ("vfio/mlx5: Add support for tracker object change event")
+Cc: Yishai Hadas <yishaih@nvidia.com>
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Alex Williamson <alex.williamson@nvidia.com>
+Reviewed-by: Kevin Tian <kevin.tian@intel.com>
+Link: https://lore.kernel.org/r/20260615191241.688297-5-alex.williamson@nvidia.com
+Signed-off-by: Alex Williamson <alex@shazbot.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vfio/pci/mlx5/cmd.h |   15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/drivers/vfio/pci/mlx5/cmd.h
++++ b/drivers/vfio/pci/mlx5/cmd.h
+@@ -155,25 +155,28 @@ struct mlx5_vhca_qp {
+ struct mlx5_vhca_page_tracker {
+       u32 id;
+       u32 pdn;
+-      u8 is_err:1;
+-      u8 object_changed:1;
++      /* Flags modified at runtime - dedicated storage unit */
++      u8 is_err;
++      u8 object_changed;
++      int status;
+       struct mlx5_uars_page *uar;
+       struct mlx5_vhca_cq cq;
+       struct mlx5_vhca_qp *host_qp;
+       struct mlx5_vhca_qp *fw_qp;
+       struct mlx5_nb nb;
+-      int status;
+ };
+ struct mlx5vf_pci_core_device {
+       struct vfio_pci_core_device core_device;
+       int vf_id;
+       u16 vhca_id;
++      /* Flags only modified on setup/release - bitfield ok */
+       u8 migrate_cap:1;
+-      u8 deferred_reset:1;
+-      u8 mdev_detach:1;
+-      u8 log_active:1;
+       u8 chunk_mode:1;
++      /* Flags modified at runtime - dedicated storage unit */
++      u8 mdev_detach;
++      u8 log_active;
++      u8 deferred_reset;
+       struct completion tracker_comp;
+       /* protect migration state */
+       struct mutex state_mutex;
diff --git a/queue-6.18/xfs-add-a-xfs_groups_to_rfsbs-helper.patch b/queue-6.18/xfs-add-a-xfs_groups_to_rfsbs-helper.patch
new file mode 100644 (file)
index 0000000..28a8057
--- /dev/null
@@ -0,0 +1,91 @@
+From stable+bounces-277346-greg=kroah.com@vger.kernel.org Sat Jul 18 17:33:33 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 11:32:56 -0400
+Subject: xfs: add a xfs_groups_to_rfsbs helper
+To: stable@vger.kernel.org
+Cc: Christoph Hellwig <hch@lst.de>, "Darrick J. Wong" <djwong@kernel.org>, Carlos Maiolino <cem@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718153257.3311310-1-sashal@kernel.org>
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 0ec73eb3f12350799c4b3fb764225f6e38b42d1e ]
+
+Plus a rtgroup wrapper and use that to avoid overflows when converting
+zone/rtg counts to block counts.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Darrick J. Wong <djwong@kernel.org>
+Signed-off-by: Carlos Maiolino <cem@kernel.org>
+Stable-dep-of: 44cccefe6574 ("xfs: only log freed extents for the current RTG in zoned growfs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/libxfs/xfs_group.h    |    9 +++++++++
+ fs/xfs/libxfs/xfs_rtgroup.h  |    8 ++++++++
+ fs/xfs/xfs_zone_gc.c         |    3 +--
+ fs/xfs/xfs_zone_space_resv.c |    8 +++-----
+ 4 files changed, 21 insertions(+), 7 deletions(-)
+
+--- a/fs/xfs/libxfs/xfs_group.h
++++ b/fs/xfs/libxfs/xfs_group.h
+@@ -98,6 +98,15 @@ xfs_group_max_blocks(
+       return xg->xg_mount->m_groups[xg->xg_type].blocks;
+ }
++static inline xfs_rfsblock_t
++xfs_groups_to_rfsbs(
++      struct xfs_mount        *mp,
++      uint32_t                nr_groups,
++      enum xfs_group_type     type)
++{
++      return (xfs_rfsblock_t)mp->m_groups[type].blocks * nr_groups;
++}
++
+ static inline xfs_fsblock_t
+ xfs_group_start_fsb(
+       struct xfs_group        *xg)
+--- a/fs/xfs/libxfs/xfs_rtgroup.h
++++ b/fs/xfs/libxfs/xfs_rtgroup.h
+@@ -371,4 +371,12 @@ static inline int xfs_initialize_rtgroup
+ # define xfs_rtgroup_get_geometry(rtg, rgeo)  (-EOPNOTSUPP)
+ #endif /* CONFIG_XFS_RT */
++static inline xfs_rfsblock_t
++xfs_rtgs_to_rfsbs(
++      struct xfs_mount        *mp,
++      uint32_t                nr_groups)
++{
++      return xfs_groups_to_rfsbs(mp, nr_groups, XG_TYPE_RTG);
++}
++
+ #endif /* __LIBXFS_RTGROUP_H */
+--- a/fs/xfs/xfs_zone_gc.c
++++ b/fs/xfs/xfs_zone_gc.c
+@@ -181,8 +181,7 @@ xfs_zoned_need_gc(
+       available = xfs_estimate_freecounter(mp, XC_FREE_RTAVAILABLE);
+       if (available <
+-          mp->m_groups[XG_TYPE_RTG].blocks *
+-          (mp->m_max_open_zones - XFS_OPEN_GC_ZONES))
++          xfs_rtgs_to_rfsbs(mp, mp->m_max_open_zones - XFS_OPEN_GC_ZONES))
+               return true;
+       free = xfs_estimate_freecounter(mp, XC_FREE_RTEXTENTS);
+--- a/fs/xfs/xfs_zone_space_resv.c
++++ b/fs/xfs/xfs_zone_space_resv.c
+@@ -54,12 +54,10 @@ xfs_zoned_default_resblks(
+ {
+       switch (ctr) {
+       case XC_FREE_RTEXTENTS:
+-              return (uint64_t)XFS_RESERVED_ZONES *
+-                      mp->m_groups[XG_TYPE_RTG].blocks +
+-                      mp->m_sb.sb_rtreserved;
++              return xfs_rtgs_to_rfsbs(mp, XFS_RESERVED_ZONES) +
++                              mp->m_sb.sb_rtreserved;
+       case XC_FREE_RTAVAILABLE:
+-              return (uint64_t)XFS_GC_ZONES *
+-                      mp->m_groups[XG_TYPE_RTG].blocks;
++              return xfs_rtgs_to_rfsbs(mp, XFS_GC_ZONES);
+       default:
+               ASSERT(0);
+               return 0;
diff --git a/queue-6.18/xfs-initialize-iomap-flags-earlier-in-xfs_bmbt_to_iomap.patch b/queue-6.18/xfs-initialize-iomap-flags-earlier-in-xfs_bmbt_to_iomap.patch
new file mode 100644 (file)
index 0000000..3c4490e
--- /dev/null
@@ -0,0 +1,45 @@
+From stable+bounces-277289-greg=kroah.com@vger.kernel.org Sat Jul 18 13:20:42 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 07:20:31 -0400
+Subject: xfs: initialize iomap->flags earlier in xfs_bmbt_to_iomap
+To: stable@vger.kernel.org
+Cc: Christoph Hellwig <hch@lst.de>, "Darrick J. Wong" <djwong@kernel.org>, Carlos Maiolino <cem@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718112031.2956124-1-sashal@kernel.org>
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 327e58826eb72f8bae9419cf1a4e722b57c85694 ]
+
+Otherwise we lose the IOMAP_IOEND_BOUNDARY assingment for writes to the
+first block in a realtime group, and could cause incorrect merges for
+such writes.
+
+Fixes: b91afef72471 ("xfs: don't merge ioends across RTGs")
+Cc: <stable@vger.kernel.org> # v6.13
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Signed-off-by: Carlos Maiolino <cem@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_iomap.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/xfs/xfs_iomap.c
++++ b/fs/xfs/xfs_iomap.c
+@@ -113,6 +113,7 @@ xfs_bmbt_to_iomap(
+               return xfs_alert_fsblock_zero(ip, imap);
+       }
++      iomap->flags = iomap_flags;
+       if (imap->br_startblock == HOLESTARTBLOCK) {
+               iomap->addr = IOMAP_NULL_ADDR;
+               iomap->type = IOMAP_HOLE;
+@@ -147,7 +148,6 @@ xfs_bmbt_to_iomap(
+               iomap->dax_dev = target->bt_daxdev;
+       else
+               iomap->bdev = target->bt_bdev;
+-      iomap->flags = iomap_flags;
+       /*
+        * If the inode is dirty for datasync purposes, let iomap know so it
diff --git a/queue-6.18/xfs-only-log-freed-extents-for-the-current-rtg-in-zoned-growfs.patch b/queue-6.18/xfs-only-log-freed-extents-for-the-current-rtg-in-zoned-growfs.patch
new file mode 100644 (file)
index 0000000..e7f981e
--- /dev/null
@@ -0,0 +1,62 @@
+From stable+bounces-277347-greg=kroah.com@vger.kernel.org Sat Jul 18 17:33:56 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 11:32:57 -0400
+Subject: xfs: only log freed extents for the current RTG in zoned growfs
+To: stable@vger.kernel.org
+Cc: Christoph Hellwig <hch@lst.de>, "Darrick J. Wong" <djwong@kernel.org>, Damien Le Moal <dlemoal@kernel.org>, Carlos Maiolino <cem@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718153257.3311310-2-sashal@kernel.org>
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 44cccefe65749821d9a13523c8b763bf1262ef73 ]
+
+Otherwise a power fail or crash during growfs could lead to an
+elevated sb_rblocks counter.
+
+Note that the step function is much simpler compared to the classic RT
+allocator as zoned RT sections must be aligned to real time group
+boundaries.
+
+Fixes: 01b71e64bb87 ("xfs: support growfs on zoned file systems")
+Cc: <stable@vger.kernel.org> # v6.15
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
+Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Carlos Maiolino <cem@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_rtalloc.c |    8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/fs/xfs/xfs_rtalloc.c
++++ b/fs/xfs/xfs_rtalloc.c
+@@ -887,8 +887,7 @@ xfs_growfs_rt_sb_fields(
+ static int
+ xfs_growfs_rt_zoned(
+-      struct xfs_rtgroup      *rtg,
+-      xfs_rfsblock_t          nrblocks)
++      struct xfs_rtgroup      *rtg)
+ {
+       struct xfs_mount        *mp = rtg_mount(rtg);
+       struct xfs_mount        *nmp;
+@@ -900,7 +899,8 @@ xfs_growfs_rt_zoned(
+        * Calculate new sb and mount fields for this round.  Also ensure the
+        * rtg_extents value is uptodate as the rtbitmap code relies on it.
+        */
+-      nmp = xfs_growfs_rt_alloc_fake_mount(mp, nrblocks,
++      nmp = xfs_growfs_rt_alloc_fake_mount(mp,
++                      xfs_rtgs_to_rfsbs(mp, rtg_rgno(rtg) + 1),
+                       mp->m_sb.sb_rextsize);
+       if (!nmp)
+               return -ENOMEM;
+@@ -1189,7 +1189,7 @@ xfs_growfs_rtg(
+       }
+       if (xfs_has_zoned(mp)) {
+-              error = xfs_growfs_rt_zoned(rtg, nrblocks);
++              error = xfs_growfs_rt_zoned(rtg);
+               goto out_rele;
+       }
diff --git a/queue-6.18/xfs-use-bio_reuse-in-the-zone-gc-code.patch b/queue-6.18/xfs-use-bio_reuse-in-the-zone-gc-code.patch
new file mode 100644 (file)
index 0000000..b5efc44
--- /dev/null
@@ -0,0 +1,57 @@
+From stable+bounces-277349-greg=kroah.com@vger.kernel.org Sat Jul 18 17:48:19 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 18 Jul 2026 11:47:52 -0400
+Subject: xfs: use bio_reuse in the zone GC code
+To: stable@vger.kernel.org
+Cc: Christoph Hellwig <hch@lst.de>, Hans Holmberg <hans.holmberg@wdc.com>, Carlos Maiolino <cmaiolino@redhat.com>, Damien Le Moal <dlemoal@kernel.org>, Carlos Maiolino <cem@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260718154759.3315619-1-sashal@kernel.org>
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 0506d32f7c52e41f6e8db7c337e0ce6374c6ffbb ]
+
+Replace our somewhat fragile code to reuse the bio, which caused a
+regression in the past with the block layer bio_reuse helper.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Hans Holmberg <hans.holmberg@wdc.com>
+Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
+Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
+Signed-off-by: Carlos Maiolino <cem@kernel.org>
+[ sashal: open-coded the bio_reuse logic at the call site as 6.18 lacks
+  commit 7ca44303f9f6 ("block: add a bio_reuse helper") ]
+Stable-dep-of: ae3692c7f440 ("xfs: add newly added RTGs to the free pool in growfs")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/xfs/xfs_zone_gc.c |   10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/fs/xfs/xfs_zone_gc.c
++++ b/fs/xfs/xfs_zone_gc.c
+@@ -812,9 +812,8 @@ xfs_zone_gc_write_chunk(
+ {
+       struct xfs_zone_gc_data *data = chunk->data;
+       struct xfs_mount        *mp = chunk->ip->i_mount;
+-      phys_addr_t             bvec_paddr =
+-              bvec_phys(bio_first_bvec_all(&chunk->bio));
+       struct xfs_gc_bio       *split_chunk;
++      unsigned short          vcnt, i;
+       if (chunk->bio.bi_status)
+               xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
+@@ -826,10 +825,11 @@ xfs_zone_gc_write_chunk(
+       WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW);
+       list_move_tail(&chunk->entry, &data->writing);
++      vcnt = chunk->bio.bi_vcnt;
+       bio_reset(&chunk->bio, mp->m_rtdev_targp->bt_bdev, REQ_OP_WRITE);
+-      bio_add_folio_nofail(&chunk->bio, chunk->scratch->folio, chunk->len,
+-                      offset_in_folio(chunk->scratch->folio, bvec_paddr));
+-
++      for (i = 0; i < vcnt; i++)
++              chunk->bio.bi_iter.bi_size += chunk->bio.bi_io_vec[i].bv_len;
++      chunk->bio.bi_vcnt = vcnt;
+       while ((split_chunk = xfs_zone_gc_split_write(data, chunk)))
+               xfs_zone_gc_submit_write(data, split_chunk);
+       xfs_zone_gc_submit_write(data, chunk);