From: Greg Kroah-Hartman Date: Tue, 21 Jul 2026 13:51:58 +0000 (+0200) Subject: 5.15-stable patches X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f9d1f90d76439605f0a17ab827eb795b739eba49;p=thirdparty%2Fkernel%2Fstable-queue.git 5.15-stable patches added patches: acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch alsa-aoa-check-snd_ctl_new1-return-value.patch audit-add-audit_log_nf_skb-helper-function.patch audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch bitops-make-bytes_to_bits-treewide-available.patch bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch bpf-convert-lpm_trie.c-to-rqspinlock.patch bpf-reject-fragmented-frames-in-devmap.patch btrfs-do-not-trim-a-device-which-is-not-writeable.patch btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch cifs-remove-unused-server-parameter-from-calc_smb_size.patch coresight-etb10-restore-atomic_t-for-shared-reading-state.patch crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch crypto-qat-validate-rsa-crt-component-lengths.patch drm-i2c-sil164-drop-no-op-remove-function.patch gpio-pca953x-make-platform-teardown-callback-return-void.patch gpio-sch-use-new-gpio-line-value-setter-callbacks.patch gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch hid-add-haptics-page-defines.patch hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch iio-invensense-remove-redundant-initialization-of-variable-period.patch iio-make-invensense-timestamp-module-generic.patch iio-move-inv_icm42600-timestamp-module-in-common.patch ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch ksmbd-track-the-connection-owning-a-byte-range-lock.patch leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch netfilter-ebtables-use-vmalloc_array-to-improve-code.patch netfilter-ebtables-zero-chainstack-array.patch pci-add-kerneldoc-for-pci_resize_resource.patch pci-altera-fix-resource-leaks-on-probe-failure.patch pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch pci-fix-restoring-bars-on-bar-resize-rollback-path.patch pci-free-saved-list-without-holding-pci_bus_sem.patch pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch pci-move-resizable-bar-code-to-rebar.c.patch pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch pci-skip-resizable-bar-restore-on-read-error.patch perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.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 proc-use-generic-setattr-for-proc-pid-net.patch regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch serial-8250_mid-disable-dma-for-selected-platforms.patch serial-8250_mid-remove-8250_pci-usage.patch smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch smb-client-harden-posix-sid-length-parsing.patch smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch smb-client-resolve-swn-tcon-from-live-registrations.patch smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch smb2-small-refactor-in-smb2_check_message.patch staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch staging-rtl8723bs-fix-indentation-issues.patch staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch staging-rtl8723bs-fix-space-issues.patch staging-rtl8723bs-fix-spaces-around-binary-operators.patch staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch staging-rtl8723bs-remove-redundant-else-branches.patch treewide-switch-rename-to-timer_delete.patch usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch usb-iowarrior-remove-inherent-race-with-minor-number.patch usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch x.509-fix-validation-of-asn.1-certificate-header.patch xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch xfs-remove-dead-code.patch xfs-remove-redundant-assignment-of-mp.patch xfs-use-null-daddr-for-unset-first-bad-log-block.patch --- diff --git a/queue-5.15/acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch b/queue-5.15/acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch new file mode 100644 index 0000000000..7881594b1b --- /dev/null +++ b/queue-5.15/acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch @@ -0,0 +1,85 @@ +From stable+bounces-273234-greg=kroah.com@vger.kernel.org Fri Jul 10 15:31:18 2026 +From: Sasha Levin +Date: Fri, 10 Jul 2026 09:30:16 -0400 +Subject: ACPI: CPPC: Suppress UBSAN warning caused by field misuse +To: stable@vger.kernel.org +Cc: Jeremy Linton , Jarred White , Easwar Hariharan , "Rafael J. Wysocki" , Sasha Levin +Message-ID: <20260710133016.79831-1-sashal@kernel.org> + +From: Jeremy Linton + +[ Upstream commit 1b1acf2dada0cc3931bb2cb9ff8832edfbee46a1 ] + +The definition of reg->access_width changes depending on the +reg->space_id type. Type ACPI_ADR_SPACE_PLATFORM_COMM uses +access_width to indicate the PCC region, which can result in a UBSAN +if the value is greater than 4. + +For example: + + UBSAN: shift-out-of-bounds in drivers/acpi/cppc_acpi.c:1090:9 + shift exponent 32 is too large for 32-bit type 'int' + CPU: 61 UID: 0 PID: 1220 Comm: (udev-worker) Not tainted 7.0.10-201.fc44.aarch64 #1 PREEMPT(lazy) + Hardware name: To be filled by O.E.M. + Call trace: + ...(trimming) + ubsan_epilogue+0x10/0x48 + __ubsan_handle_shift_out_of_bounds+0xdc/0x1e0 + cpc_write+0x4d0/0x670 + cppc_set_perf+0x18c/0x490 + cppc_cpufreq_cpu_init+0x1c8/0x380 [cppc_cpufreq] + ... (trimming) + +Lets fix this by validating the region type, as well as whether +access_width has a value. Then since we are returning bit_width +directly for ACPI_ADR_SPACE_PLATFORM_COMM, drop the code correcting +the size. + +Fixes: 2f4a4d63a193 ("ACPI: CPPC: Use access_width over bit_width for system memory accesses") +Signed-off-by: Jeremy Linton +Tested-by: Jarred White +Reviewed-by: Jarred White +Reviewed-by: Easwar Hariharan +Cc: All applicable +Link: https://patch.msgid.link/20260601235808.1113137-1-jeremy.linton@arm.com +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/acpi/cppc_acpi.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/acpi/cppc_acpi.c ++++ b/drivers/acpi/cppc_acpi.c +@@ -161,8 +161,13 @@ show_cppc_data(cppc_get_perf_caps, cppc_ + show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, reference_perf); + show_cppc_data(cppc_get_perf_ctrs, cppc_perf_fb_ctrs, wraparound_time); + +-/* Check for valid access_width, otherwise, fallback to using bit_width */ +-#define GET_BIT_WIDTH(reg) ((reg)->access_width ? (8 << ((reg)->access_width - 1)) : (reg)->bit_width) ++/* ++ * PCC reuses the access_width field as the subspace id, so only decode access ++ * size for non-PCC registers. Otherwise, use the bit_width. ++ */ ++#define GET_BIT_WIDTH(reg) (((reg)->access_width && \ ++ (reg)->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) ? \ ++ (8 << ((reg)->access_width - 1)) : (reg)->bit_width) + + /* Shift and apply the mask for CPC reads/writes */ + #define MASK_VAL_READ(reg, val) (((val) >> (reg)->bit_offset) & \ +@@ -970,7 +975,6 @@ static int cpc_read(int cpu, struct cpc_ + * by the bit width field; the access size is used to indicate + * the PCC subspace id. + */ +- size = reg->bit_width; + vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id); + } + else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) +@@ -1030,7 +1034,6 @@ static int cpc_write(int cpu, struct cpc + * by the bit width field; the access size is used to indicate + * the PCC subspace id. + */ +- size = reg->bit_width; + vaddr = GET_PCC_VADDR(reg->address, pcc_ss_id); + } + else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) diff --git a/queue-5.15/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch b/queue-5.15/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch new file mode 100644 index 0000000000..8eebaa8b72 --- /dev/null +++ b/queue-5.15/acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch @@ -0,0 +1,103 @@ +From stable+bounces-273253-greg=kroah.com@vger.kernel.org Fri Jul 10 16:55:10 2026 +From: Sasha Levin +Date: Fri, 10 Jul 2026 10:38:56 -0400 +Subject: ACPI: NFIT: core: Fix acpi_nfit_init() error cleanup +To: stable@vger.kernel.org +Cc: "Rafael J. Wysocki" , Dave Jiang , Sasha Levin +Message-ID: <20260710143856.184042-1-sashal@kernel.org> + +From: "Rafael J. Wysocki" + +[ 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 +Cc: All applicable +Reviewed-by: Dave Jiang +Link: https://patch.msgid.link/1963615.tdWV9SEqCh@rafael.j.wysocki +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/acpi/nfit/core.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/drivers/acpi/nfit/core.c ++++ b/drivers/acpi/nfit/core.c +@@ -3445,6 +3445,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) +@@ -3669,7 +3671,10 @@ static void acpi_nfit_put_table(void *ta + 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 +@@ -3684,6 +3689,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 +@@ -3751,13 +3757,14 @@ static int acpi_nfit_add(struct acpi_dev + sz - sizeof(struct acpi_table_nfit)); + + if (rc) +- return rc; +- return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc); ++ acpi_nfit_shutdown(acpi_desc); ++ ++ return rc; + } + + static int acpi_nfit_remove(struct acpi_device *adev) + { +- /* see acpi_nfit_unregister */ ++ acpi_nfit_shutdown(dev_get_drvdata(&adev->dev)); + return 0; + } + diff --git a/queue-5.15/alsa-aoa-check-snd_ctl_new1-return-value.patch b/queue-5.15/alsa-aoa-check-snd_ctl_new1-return-value.patch new file mode 100644 index 0000000000..c6b093df1a --- /dev/null +++ b/queue-5.15/alsa-aoa-check-snd_ctl_new1-return-value.patch @@ -0,0 +1,61 @@ +From stable+bounces-274081-greg=kroah.com@vger.kernel.org Tue Jul 14 04:10:08 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 22:09:11 -0400 +Subject: ALSA: aoa: check snd_ctl_new1() return value +To: stable@vger.kernel.org +Cc: Zhao Dongdong , Takashi Iwai , Sasha Levin +Message-ID: <20260714020911.2355950-1-sashal@kernel.org> + +From: Zhao Dongdong + +[ Upstream commit 8df560fefe6fed6a20b7e06720eeaeccec349ac0 ] + +snd_ctl_new1() can return NULL when memory allocation fails. In +layout.c, the function does not check the return value before +dereferencing ctl->id.name or passing to aoa_snd_ctl_add(), which can +lead to a NULL pointer dereference. + +Add NULL checks after snd_ctl_new1() calls and return early if any +fails. + +Assisted-by: Opencode:DeepSeek-V4-Flash +Cc: stable@vger.kernel.org +Fixes: f3d9478b2ce4 ("[ALSA] snd-aoa: add snd-aoa") +Signed-off-by: Zhao Dongdong +Link: https://patch.msgid.link/tencent_35F3A25FEEBF190A2E15ED787754C57E3708@qq.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/aoa/fabrics/layout.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/sound/aoa/fabrics/layout.c ++++ b/sound/aoa/fabrics/layout.c +@@ -947,6 +947,8 @@ static void layout_attached_codec(struct + if (lineout == 1) + ldev->gpio.methods->set_lineout(codec->gpio, 1); + ctl = snd_ctl_new1(&lineout_ctl, codec->gpio); ++ if (!ctl) ++ return; + if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) + strscpy(ctl->id.name, + "Headphone Switch", sizeof(ctl->id.name)); +@@ -961,6 +963,8 @@ static void layout_attached_codec(struct + if (ldev->have_lineout_detect) { + ctl = snd_ctl_new1(&lineout_detect_choice, + ldev); ++ if (!ctl) ++ return; + if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) + strscpy(ctl->id.name, + "Headphone Detect Autoswitch", +@@ -968,6 +972,8 @@ static void layout_attached_codec(struct + aoa_snd_ctl_add(ctl); + ctl = snd_ctl_new1(&lineout_detected, + ldev); ++ if (!ctl) ++ return; + if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) + strscpy(ctl->id.name, + "Headphone Detected", diff --git a/queue-5.15/audit-add-audit_log_nf_skb-helper-function.patch b/queue-5.15/audit-add-audit_log_nf_skb-helper-function.patch new file mode 100644 index 0000000000..c6fecd64ef --- /dev/null +++ b/queue-5.15/audit-add-audit_log_nf_skb-helper-function.patch @@ -0,0 +1,296 @@ +From stable+bounces-278563-greg=kroah.com@vger.kernel.org Tue Jul 21 13:50:42 2026 +From: Sasha Levin +Date: Tue, 21 Jul 2026 07:47:02 -0400 +Subject: audit: add audit_log_nf_skb helper function +To: stable@vger.kernel.org +Cc: Ricardo Robaina , Florian Westphal , Paul Moore , Sasha Levin +Message-ID: <20260721114703.3688208-1-sashal@kernel.org> + +From: Ricardo Robaina + +[ Upstream commit f19590b07cb620be1fcd5474c49515e21a05d406 ] + +Netfilter code (net/netfilter/nft_log.c and net/netfilter/xt_AUDIT.c) +have to be kept in sync. Both source files had duplicated versions of +audit_ip4() and audit_ip6() functions, which can result in lack of +consistency and/or duplicated work. + +This patch adds a helper function in audit.c that can be called by +netfilter code commonly, aiming to improve maintainability and +consistency. + +Suggested-by: Florian Westphal +Suggested-by: Paul Moore +Signed-off-by: Ricardo Robaina +Acked-by: Florian Westphal +Signed-off-by: Paul Moore +Stable-dep-of: 65dfde57d1e2 ("audit: fix potential integer overflow in audit_log_n_hex()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/audit.h | 8 +++++ + kernel/audit.c | 64 +++++++++++++++++++++++++++++++++++++++++++++++ + net/netfilter/nft_log.c | 58 ------------------------------------------ + net/netfilter/xt_AUDIT.c | 58 ------------------------------------------ + 4 files changed, 74 insertions(+), 114 deletions(-) + +--- a/include/linux/audit.h ++++ b/include/linux/audit.h +@@ -186,6 +186,8 @@ extern void audit_log_lost(const ch + + extern int audit_log_task_context(struct audit_buffer *ab); + extern void audit_log_task_info(struct audit_buffer *ab); ++extern int audit_log_nf_skb(struct audit_buffer *ab, ++ const struct sk_buff *skb, u8 nfproto); + + extern int audit_update_lsm_rules(void); + +@@ -251,6 +253,12 @@ static inline int audit_log_task_context + static inline void audit_log_task_info(struct audit_buffer *ab) + { } + ++static inline int audit_log_nf_skb(struct audit_buffer *ab, ++ const struct sk_buff *skb, u8 nfproto) ++{ ++ return 0; ++} ++ + static inline kuid_t audit_get_loginuid(struct task_struct *tsk) + { + return INVALID_UID; +--- a/kernel/audit.c ++++ b/kernel/audit.c +@@ -59,6 +59,8 @@ + #include + #include + #include ++#include ++#include + + #include "audit.h" + +@@ -2307,6 +2309,68 @@ void audit_log_path_denied(int type, con + audit_log_end(ab); + } + ++int audit_log_nf_skb(struct audit_buffer *ab, ++ const struct sk_buff *skb, u8 nfproto) ++{ ++ /* find the IP protocol in the case of NFPROTO_BRIDGE */ ++ if (nfproto == NFPROTO_BRIDGE) { ++ switch (eth_hdr(skb)->h_proto) { ++ case htons(ETH_P_IP): ++ nfproto = NFPROTO_IPV4; ++ break; ++ case htons(ETH_P_IPV6): ++ nfproto = NFPROTO_IPV6; ++ break; ++ default: ++ goto unknown_proto; ++ } ++ } ++ ++ switch (nfproto) { ++ case NFPROTO_IPV4: { ++ struct iphdr iph; ++ const struct iphdr *ih; ++ ++ ih = skb_header_pointer(skb, skb_network_offset(skb), ++ sizeof(iph), &iph); ++ if (!ih) ++ return -ENOMEM; ++ ++ audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu", ++ &ih->saddr, &ih->daddr, ih->protocol); ++ break; ++ } ++ case NFPROTO_IPV6: { ++ struct ipv6hdr iph; ++ const struct ipv6hdr *ih; ++ u8 nexthdr; ++ __be16 frag_off; ++ ++ ih = skb_header_pointer(skb, skb_network_offset(skb), ++ sizeof(iph), &iph); ++ if (!ih) ++ return -ENOMEM; ++ ++ nexthdr = ih->nexthdr; ++ ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(iph), ++ &nexthdr, &frag_off); ++ ++ audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu", ++ &ih->saddr, &ih->daddr, nexthdr); ++ break; ++ } ++ default: ++ goto unknown_proto; ++ } ++ ++ return 0; ++ ++unknown_proto: ++ audit_log_format(ab, " saddr=? daddr=? proto=?"); ++ return -EPFNOSUPPORT; ++} ++EXPORT_SYMBOL(audit_log_nf_skb); ++ + /* global counter which is incremented every time something logs in */ + static atomic_t session_id = ATOMIC_INIT(0); + +--- a/net/netfilter/nft_log.c ++++ b/net/netfilter/nft_log.c +@@ -26,46 +26,10 @@ struct nft_log { + char *prefix; + }; + +-static bool audit_ip4(struct audit_buffer *ab, struct sk_buff *skb) +-{ +- struct iphdr _iph; +- const struct iphdr *ih; +- +- ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_iph), &_iph); +- if (!ih) +- return false; +- +- audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu", +- &ih->saddr, &ih->daddr, ih->protocol); +- +- return true; +-} +- +-static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb) +-{ +- struct ipv6hdr _ip6h; +- const struct ipv6hdr *ih; +- u8 nexthdr; +- __be16 frag_off; +- +- ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h); +- if (!ih) +- return false; +- +- nexthdr = ih->nexthdr; +- ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), &nexthdr, &frag_off); +- +- audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu", +- &ih->saddr, &ih->daddr, nexthdr); +- +- return true; +-} +- + static void nft_log_eval_audit(const struct nft_pktinfo *pkt) + { + struct sk_buff *skb = pkt->skb; + struct audit_buffer *ab; +- int fam = -1; + + if (!audit_enabled) + return; +@@ -76,27 +40,7 @@ static void nft_log_eval_audit(const str + + audit_log_format(ab, "mark=%#x", skb->mark); + +- switch (nft_pf(pkt)) { +- case NFPROTO_BRIDGE: +- switch (eth_hdr(skb)->h_proto) { +- case htons(ETH_P_IP): +- fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1; +- break; +- case htons(ETH_P_IPV6): +- fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1; +- break; +- } +- break; +- case NFPROTO_IPV4: +- fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1; +- break; +- case NFPROTO_IPV6: +- fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1; +- break; +- } +- +- if (fam == -1) +- audit_log_format(ab, " saddr=? daddr=? proto=-1"); ++ audit_log_nf_skb(ab, skb, nft_pf(pkt)); + + audit_log_end(ab); + } +--- a/net/netfilter/xt_AUDIT.c ++++ b/net/netfilter/xt_AUDIT.c +@@ -28,46 +28,10 @@ MODULE_ALIAS("ip6t_AUDIT"); + MODULE_ALIAS("ebt_AUDIT"); + MODULE_ALIAS("arpt_AUDIT"); + +-static bool audit_ip4(struct audit_buffer *ab, struct sk_buff *skb) +-{ +- struct iphdr _iph; +- const struct iphdr *ih; +- +- ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_iph), &_iph); +- if (!ih) +- return false; +- +- audit_log_format(ab, " saddr=%pI4 daddr=%pI4 proto=%hhu", +- &ih->saddr, &ih->daddr, ih->protocol); +- +- return true; +-} +- +-static bool audit_ip6(struct audit_buffer *ab, struct sk_buff *skb) +-{ +- struct ipv6hdr _ip6h; +- const struct ipv6hdr *ih; +- u8 nexthdr; +- __be16 frag_off; +- +- ih = skb_header_pointer(skb, skb_network_offset(skb), sizeof(_ip6h), &_ip6h); +- if (!ih) +- return false; +- +- nexthdr = ih->nexthdr; +- ipv6_skip_exthdr(skb, skb_network_offset(skb) + sizeof(_ip6h), &nexthdr, &frag_off); +- +- audit_log_format(ab, " saddr=%pI6c daddr=%pI6c proto=%hhu", +- &ih->saddr, &ih->daddr, nexthdr); +- +- return true; +-} +- + static unsigned int + audit_tg(struct sk_buff *skb, const struct xt_action_param *par) + { + struct audit_buffer *ab; +- int fam = -1; + + if (audit_enabled == AUDIT_OFF) + goto errout; +@@ -77,27 +41,7 @@ audit_tg(struct sk_buff *skb, const stru + + audit_log_format(ab, "mark=%#x", skb->mark); + +- switch (xt_family(par)) { +- case NFPROTO_BRIDGE: +- switch (eth_hdr(skb)->h_proto) { +- case htons(ETH_P_IP): +- fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1; +- break; +- case htons(ETH_P_IPV6): +- fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1; +- break; +- } +- break; +- case NFPROTO_IPV4: +- fam = audit_ip4(ab, skb) ? NFPROTO_IPV4 : -1; +- break; +- case NFPROTO_IPV6: +- fam = audit_ip6(ab, skb) ? NFPROTO_IPV6 : -1; +- break; +- } +- +- if (fam == -1) +- audit_log_format(ab, " saddr=? daddr=? proto=-1"); ++ audit_log_nf_skb(ab, skb, xt_family(par)); + + audit_log_end(ab); + diff --git a/queue-5.15/audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch b/queue-5.15/audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch new file mode 100644 index 0000000000..b5b64875f1 --- /dev/null +++ b/queue-5.15/audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch @@ -0,0 +1,66 @@ +From stable+bounces-278564-greg=kroah.com@vger.kernel.org Tue Jul 21 13:50:47 2026 +From: Sasha Levin +Date: Tue, 21 Jul 2026 07:47:03 -0400 +Subject: audit: fix potential integer overflow in audit_log_n_hex() +To: stable@vger.kernel.org +Cc: Ricardo Robaina , Richard Guy Briggs , Paul Moore , Sasha Levin +Message-ID: <20260721114703.3688208-2-sashal@kernel.org> + +From: Ricardo Robaina + +[ Upstream commit 65dfde57d1e29ce2b76fc23dd565eccd5c0bc0f0 ] + +The function calculates new_len as len << 1 for hex encoding. This +has two overflow risks: the shift itself can overflow when len is +large, and the result can be truncated when assigned to new_len +(declared as int) from the size_t calculation. + +Fix by using check_shl_overflow() to catch shift overflow and +changing new_len and loop counter i to size_t to prevent truncation. + +Cc: stable@vger.kernel.org +Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings") +Reviewed-by: Richard Guy Briggs +Signed-off-by: Ricardo Robaina +[PM: remove vertical whitspace noise] +Signed-off-by: Paul Moore +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/audit.c | 11 +++++++++-- + 1 file changed, 9 insertions(+), 2 deletions(-) + +--- a/kernel/audit.c ++++ b/kernel/audit.c +@@ -61,6 +61,7 @@ + #include + #include + #include ++#include + + #include "audit.h" + +@@ -2037,7 +2038,8 @@ void audit_log_format(struct audit_buffe + void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, + size_t len) + { +- int i, avail, new_len; ++ int avail; ++ size_t i, new_len; + unsigned char *ptr; + struct sk_buff *skb; + +@@ -2047,7 +2049,12 @@ void audit_log_n_hex(struct audit_buffer + BUG_ON(!ab->skb); + skb = ab->skb; + avail = skb_tailroom(skb); +- new_len = len<<1; ++ ++ if (check_shl_overflow(len, 1, &new_len)) { ++ audit_log_format(ab, "?"); ++ return; ++ } ++ + if (new_len >= avail) { + /* Round the buffer request up to the next multiple */ + new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1); diff --git a/queue-5.15/bitops-make-bytes_to_bits-treewide-available.patch b/queue-5.15/bitops-make-bytes_to_bits-treewide-available.patch new file mode 100644 index 0000000000..2f1d217102 --- /dev/null +++ b/queue-5.15/bitops-make-bytes_to_bits-treewide-available.patch @@ -0,0 +1,90 @@ +From stable+bounces-274079-greg=kroah.com@vger.kernel.org Tue Jul 14 04:10:00 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 22:09:07 -0400 +Subject: bitops: make BYTES_TO_BITS() treewide-available +To: stable@vger.kernel.org +Cc: Alexander Lobakin , Andy Shevchenko , Przemek Kitszel , Yury Norov , "David S. Miller" , Sasha Levin +Message-ID: <20260714020908.2355876-1-sashal@kernel.org> + +From: Alexander Lobakin + +[ Upstream commit 7d8296b250f2eed73f1758607926d4d258dea5d4 ] + +Avoid open-coding that simple expression each time by moving +BYTES_TO_BITS() from the probes code to to export +it to the rest of the kernel. +Simplify the macro while at it. `BITS_PER_LONG / sizeof(long)` always +equals to %BITS_PER_BYTE, regardless of the target architecture. +Do the same for the tools ecosystem as well (incl. its version of +bitops.h). The previous implementation had its implicit type of long, +while the new one is int, so adjust the format literal accordingly in +the perf code. + +Suggested-by: Andy Shevchenko +Reviewed-by: Przemek Kitszel +Acked-by: Yury Norov +Signed-off-by: Alexander Lobakin +Signed-off-by: David S. Miller +Stable-dep-of: 55052184ac90 ("iio: common: st_sensors: honour channel endianness in read_axis_data") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bitops.h | 2 ++ + kernel/trace/trace_probe.c | 2 -- + tools/include/linux/bitops.h | 2 ++ + tools/perf/util/probe-finder.c | 4 +--- + 4 files changed, 5 insertions(+), 5 deletions(-) + +--- a/include/linux/bitops.h ++++ b/include/linux/bitops.h +@@ -21,6 +21,8 @@ + #define BITS_TO_U32(nr) __KERNEL_DIV_ROUND_UP(nr, BITS_PER_TYPE(u32)) + #define BITS_TO_BYTES(nr) __KERNEL_DIV_ROUND_UP(nr, BITS_PER_TYPE(char)) + ++#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_BYTE) ++ + extern unsigned int __sw_hweight8(unsigned int w); + extern unsigned int __sw_hweight16(unsigned int w); + extern unsigned int __sw_hweight32(unsigned int w); +--- a/kernel/trace/trace_probe.c ++++ b/kernel/trace/trace_probe.c +@@ -531,8 +531,6 @@ parse_probe_arg(char *arg, const struct + return ret; + } + +-#define BYTES_TO_BITS(nb) ((BITS_PER_LONG * (nb)) / sizeof(long)) +- + /* Bitfield type needs to be parsed into a fetch function */ + static int __parse_bitfield_probe_arg(const char *bf, + const struct fetch_type *t, +--- a/tools/include/linux/bitops.h ++++ b/tools/include/linux/bitops.h +@@ -20,6 +20,8 @@ + #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(u32)) + #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(char)) + ++#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_BYTE) ++ + extern unsigned int __sw_hweight8(unsigned int w); + extern unsigned int __sw_hweight16(unsigned int w); + extern unsigned int __sw_hweight32(unsigned int w); +--- a/tools/perf/util/probe-finder.c ++++ b/tools/perf/util/probe-finder.c +@@ -304,8 +304,6 @@ static_var: + return ret2; + } + +-#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long)) +- + static int convert_variable_type(Dwarf_Die *vr_die, + struct probe_trace_arg *tvar, + const char *cast, bool user_access) +@@ -335,7 +333,7 @@ static int convert_variable_type(Dwarf_D + total = dwarf_bytesize(vr_die); + if (boffs < 0 || total < 0) + return -ENOENT; +- ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs, ++ ret = snprintf(buf, 16, "b%d@%d/%d", bsize, boffs, + BYTES_TO_BITS(total)); + goto formatted; + } diff --git a/queue-5.15/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch b/queue-5.15/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch new file mode 100644 index 0000000000..58b781809a --- /dev/null +++ b/queue-5.15/bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch @@ -0,0 +1,67 @@ +From stable+bounces-275103-greg=kroah.com@vger.kernel.org Thu Jul 16 02:04:20 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 20:04:13 -0400 +Subject: Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock +To: stable@vger.kernel.org +Cc: Runyu Xiao , Luiz Augusto von Dentz , Sasha Levin +Message-ID: <20260716000413.561625-3-sashal@kernel.org> + +From: Runyu Xiao + +[ Upstream commit 2641a9e0a1dd4af2e21995470a21d55dd35e5203 ] + +l2cap_conn_del() takes conn->lock and then calls cancel_work_sync() for +pending_rx_work. process_pending_rx() takes the same mutex, so teardown +can deadlock against the worker it is flushing. + +This issue was found by our static analysis tool and then manually +reviewed against the current tree. + +The grounded PoC kept the l2cap_conn_ready() -> queue_work(..., +&conn->pending_rx_work) submit path, the l2cap_conn_del() -> +cancel_work_sync(&conn->pending_rx_work) teardown path, and the +process_pending_rx() -> mutex_lock(&conn->lock) worker edge. Lockdep +reported: + + WARNING: possible circular locking dependency detected + process_pending_rx+0x21/0x2a [vuln_msv] + l2cap_conn_del.constprop.0+0x3f/0x4e [vuln_msv] + *** DEADLOCK *** + +Cancel pending_rx_work before taking conn->lock, matching the existing +lock-before-drain ordering used for the two delayed works in the same +teardown path. The pending_rx queue is still purged after the work has +been cancelled and conn->lock has been acquired. + +Fixes: 7ab56c3a6ecc ("Bluetooth: Fix deadlock in l2cap_conn_del()") +Cc: stable@vger.kernel.org +Signed-off-by: Runyu Xiao +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/l2cap_core.c | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -1899,17 +1899,11 @@ static void l2cap_conn_del(struct hci_co + cancel_delayed_work_sync(&conn->info_timer); + cancel_work_sync(&conn->id_addr_update_work); + ++ cancel_work_sync(&conn->pending_rx_work); ++ + kfree_skb(conn->rx_skb); + + skb_queue_purge(&conn->pending_rx); +- +- /* We can not call flush_work(&conn->pending_rx_work) here since we +- * might block if we are running on a worker from the same workqueue +- * pending_rx_work is waiting on. +- */ +- if (work_pending(&conn->pending_rx_work)) +- cancel_work_sync(&conn->pending_rx_work); +- + ida_destroy(&conn->tx_ida); + + l2cap_unregister_all_users(conn); diff --git a/queue-5.15/bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch b/queue-5.15/bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch new file mode 100644 index 0000000000..57c317edf7 --- /dev/null +++ b/queue-5.15/bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch @@ -0,0 +1,65 @@ +From stable+bounces-275102-greg=kroah.com@vger.kernel.org Thu Jul 16 02:04:22 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 20:04:12 -0400 +Subject: Bluetooth: L2CAP: Fix deadlock in l2cap_conn_del() +To: stable@vger.kernel.org +Cc: Hyunwoo Kim , Luiz Augusto von Dentz , Sasha Levin +Message-ID: <20260716000413.561625-2-sashal@kernel.org> + +From: Hyunwoo Kim + +[ Upstream commit 00fdebbbc557a2fc21321ff2eaa22fd70c078608 ] + +l2cap_conn_del() calls cancel_delayed_work_sync() for both info_timer +and id_addr_timer while holding conn->lock. However, the work functions +l2cap_info_timeout() and l2cap_conn_update_id_addr() both acquire +conn->lock, creating a potential AB-BA deadlock if the work is already +executing when l2cap_conn_del() takes the lock. + +Move the work cancellations before acquiring conn->lock and use +disable_delayed_work_sync() to additionally prevent the works from +being rearmed after cancellation, consistent with the pattern used in +hci_conn_del(). + +Fixes: ab4eedb790ca ("Bluetooth: L2CAP: Fix corrupted list in hci_chan_del") +Signed-off-by: Hyunwoo Kim +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: 2641a9e0a1dd ("Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/l2cap_core.c | 9 +++------ + 1 file changed, 3 insertions(+), 6 deletions(-) + +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -1896,6 +1896,9 @@ static void l2cap_conn_del(struct hci_co + + BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); + ++ cancel_delayed_work_sync(&conn->info_timer); ++ cancel_work_sync(&conn->id_addr_update_work); ++ + kfree_skb(conn->rx_skb); + + skb_queue_purge(&conn->pending_rx); +@@ -1909,9 +1912,6 @@ static void l2cap_conn_del(struct hci_co + + ida_destroy(&conn->tx_ida); + +- if (work_pending(&conn->id_addr_update_work)) +- cancel_work_sync(&conn->id_addr_update_work); +- + l2cap_unregister_all_users(conn); + + /* Force the connection to be immediately dropped */ +@@ -1936,9 +1936,6 @@ static void l2cap_conn_del(struct hci_co + + hci_chan_del(conn->hchan); + +- if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) +- cancel_delayed_work_sync(&conn->info_timer); +- + hcon->l2cap_data = NULL; + conn->hchan = NULL; + l2cap_conn_put(conn); diff --git a/queue-5.15/bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch b/queue-5.15/bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch new file mode 100644 index 0000000000..b7642453ee --- /dev/null +++ b/queue-5.15/bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch @@ -0,0 +1,154 @@ +From stable+bounces-275101-greg=kroah.com@vger.kernel.org Thu Jul 16 02:04:19 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 20:04:11 -0400 +Subject: Bluetooth: L2CAP: Fix not tracking outstanding TX ident +To: stable@vger.kernel.org +Cc: Luiz Augusto von Dentz , Paul Menzel , Sasha Levin +Message-ID: <20260716000413.561625-1-sashal@kernel.org> + +From: Luiz Augusto von Dentz + +[ Upstream commit 6c3ea155e5ee3e56606233acde8309afda66d483 ] + +This attempts to proper track outstanding request by using struct ida +and allocating from it in l2cap_get_ident using ida_alloc_range which +would reuse ids as they are free, then upon completion release +the id using ida_free. + +This fixes the qualification test case L2CAP/COS/CED/BI-29-C which +attempts to check if the host stack is able to work after 256 attempts +to connect which requires Ident field to use the full range of possible +values in order to pass the test. + +Link: https://github.com/bluez/bluez/issues/1829 +Signed-off-by: Luiz Augusto von Dentz +Reviewed-by: Paul Menzel +Stable-dep-of: 2641a9e0a1dd ("Bluetooth: L2CAP: cancel pending_rx_work before taking conn->lock") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/net/bluetooth/l2cap.h | 3 -- + net/bluetooth/l2cap_core.c | 53 ++++++++++++++++++++++++++++++------------ + 2 files changed, 40 insertions(+), 16 deletions(-) + +--- a/include/net/bluetooth/l2cap.h ++++ b/include/net/bluetooth/l2cap.h +@@ -691,8 +691,7 @@ struct l2cap_conn { + + struct sk_buff *rx_skb; + __u32 rx_len; +- __u8 tx_ident; +- struct mutex ident_lock; ++ struct ida tx_ida; + + struct sk_buff_head pending_rx; + struct work_struct pending_rx_work; +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -937,26 +937,18 @@ int l2cap_chan_check_security(struct l2c + initiator); + } + +-static u8 l2cap_get_ident(struct l2cap_conn *conn) ++static int l2cap_get_ident(struct l2cap_conn *conn) + { +- u8 id; ++ /* LE link does not support tools like l2ping so use the full range */ ++ if (conn->hcon->type == LE_LINK) ++ return ida_alloc_range(&conn->tx_ida, 1, 255, GFP_ATOMIC); + + /* Get next available identificator. + * 1 - 128 are used by kernel. + * 129 - 199 are reserved. + * 200 - 254 are used by utilities like l2ping, etc. + */ +- +- mutex_lock(&conn->ident_lock); +- +- if (++conn->tx_ident > 128) +- conn->tx_ident = 1; +- +- id = conn->tx_ident; +- +- mutex_unlock(&conn->ident_lock); +- +- return id; ++ return ida_alloc_range(&conn->tx_ida, 1, 128, GFP_ATOMIC); + } + + static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, +@@ -1915,6 +1907,8 @@ static void l2cap_conn_del(struct hci_co + if (work_pending(&conn->pending_rx_work)) + cancel_work_sync(&conn->pending_rx_work); + ++ ida_destroy(&conn->tx_ida); ++ + if (work_pending(&conn->id_addr_update_work)) + cancel_work_sync(&conn->id_addr_update_work); + +@@ -5778,12 +5772,41 @@ unlock: + return err; + } + ++static void l2cap_put_ident(struct l2cap_conn *conn, u8 code, u8 id) ++{ ++ int ret; ++ ++ switch (code) { ++ case L2CAP_COMMAND_REJ: ++ case L2CAP_CONN_RSP: ++ case L2CAP_CONF_RSP: ++ case L2CAP_DISCONN_RSP: ++ case L2CAP_ECHO_RSP: ++ case L2CAP_INFO_RSP: ++ case L2CAP_CONN_PARAM_UPDATE_RSP: ++ case L2CAP_ECRED_CONN_RSP: ++ case L2CAP_ECRED_RECONF_RSP: ++ /* The remote may send bogus ids that would make ida_free ++ * generate warnings, so only free ids that are actually ++ * allocated: probing the exact id returns -ENOSPC when it ++ * is in use, otherwise the probe allocated it and freeing ++ * is safe either way. Only on -ENOMEM is the id known to ++ * be unallocated and the free must be skipped. ++ */ ++ ret = ida_alloc_range(&conn->tx_ida, id, id, GFP_ATOMIC); ++ if (ret >= 0 || ret == -ENOSPC) ++ ida_free(&conn->tx_ida, id); ++ } ++} ++ + static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) + { + int err = 0; + ++ l2cap_put_ident(conn, cmd->code, cmd->ident); ++ + switch (cmd->code) { + case L2CAP_COMMAND_REJ: + l2cap_command_rej(conn, cmd, cmd_len, data); +@@ -6522,6 +6545,8 @@ static inline int l2cap_le_sig_cmd(struc + { + int err = 0; + ++ l2cap_put_ident(conn, cmd->code, cmd->ident); ++ + switch (cmd->code) { + case L2CAP_COMMAND_REJ: + l2cap_le_command_rej(conn, cmd, cmd_len, data); +@@ -8075,13 +8100,13 @@ static struct l2cap_conn *l2cap_conn_add + hci_dev_test_flag(hcon->hdev, HCI_FORCE_BREDR_SMP))) + conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR; + +- mutex_init(&conn->ident_lock); + mutex_init(&conn->chan_lock); + + INIT_LIST_HEAD(&conn->chan_l); + INIT_LIST_HEAD(&conn->users); + + INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout); ++ ida_init(&conn->tx_ida); + + skb_queue_head_init(&conn->pending_rx); + INIT_WORK(&conn->pending_rx_work, process_pending_rx); diff --git a/queue-5.15/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch b/queue-5.15/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch new file mode 100644 index 0000000000..d15be05125 --- /dev/null +++ b/queue-5.15/bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch @@ -0,0 +1,118 @@ +From stable+bounces-277389-greg=kroah.com@vger.kernel.org Sat Jul 18 23:51:57 2026 +From: Sasha Levin +Date: Sat, 18 Jul 2026 17:50:21 -0400 +Subject: bpf: Allow LPM map access from sleepable BPF programs +To: stable@vger.kernel.org +Cc: Vlad Poenaru , Emil Tsalapatis , Alexei Starovoitov , Sasha Levin +Message-ID: <20260718215021.3447372-3-sashal@kernel.org> + +From: Vlad Poenaru + +[ 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 +Reviewed-by: Emil Tsalapatis +Link: https://lore.kernel.org/r/20260609135558.193287-2-vlad.wing@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -235,7 +235,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; +@@ -269,7 +269,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) +@@ -349,7 +349,7 @@ static int trie_update_elem(struct bpf_m + */ + 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 || +@@ -474,7 +474,7 @@ static int trie_delete_elem(struct bpf_m + 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-5.15/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch b/queue-5.15/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch new file mode 100644 index 0000000000..6b16544e53 --- /dev/null +++ b/queue-5.15/bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch @@ -0,0 +1,153 @@ +From stable+bounces-277388-greg=kroah.com@vger.kernel.org Sat Jul 18 23:51:52 2026 +From: Sasha Levin +Date: Sat, 18 Jul 2026 17:50:20 -0400 +Subject: bpf: Consistently use bpf_rcu_lock_held() everywhere +To: stable@vger.kernel.org +Cc: Andrii Nakryiko , Daniel Borkmann , Jiri Olsa , Sasha Levin +Message-ID: <20260718215021.3447372-2-sashal@kernel.org> + +From: Andrii Nakryiko + +[ 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 +Signed-off-by: Daniel Borkmann +Acked-by: Jiri Olsa +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 +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 4 ++++ + kernel/bpf/hashtab.c | 21 +++++++-------------- + kernel/bpf/helpers.c | 9 +++------ + 3 files changed, 14 insertions(+), 20 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + struct bpf_verifier_env; + struct bpf_verifier_log; +@@ -1482,6 +1483,9 @@ out: + _ret; \ + }) + ++#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/kernel/bpf/hashtab.c ++++ b/kernel/bpf/hashtab.c +@@ -642,8 +642,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; + +@@ -1047,8 +1046,7 @@ static int htab_map_update_elem(struct b + /* 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; + +@@ -1149,8 +1147,7 @@ static int htab_lru_map_update_elem(stru + /* 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; + +@@ -1218,8 +1215,7 @@ static int __htab_percpu_map_update_elem + /* 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; + +@@ -1273,8 +1269,7 @@ static int __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; + +@@ -1349,8 +1344,7 @@ static int htab_map_delete_elem(struct b + 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; + +@@ -1385,8 +1379,7 @@ static int htab_lru_map_delete_elem(stru + 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 +@@ -30,8 +30,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); + } + +@@ -47,8 +46,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); + } + +@@ -65,8 +63,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); + } + diff --git a/queue-5.15/bpf-convert-lpm_trie.c-to-rqspinlock.patch b/queue-5.15/bpf-convert-lpm_trie.c-to-rqspinlock.patch new file mode 100644 index 0000000000..2f54e94514 --- /dev/null +++ b/queue-5.15/bpf-convert-lpm_trie.c-to-rqspinlock.patch @@ -0,0 +1,54 @@ +From stable+bounces-277387-greg=kroah.com@vger.kernel.org Sat Jul 18 23:51:13 2026 +From: Sasha Levin +Date: Sat, 18 Jul 2026 17:50:19 -0400 +Subject: bpf: Convert lpm_trie.c to rqspinlock +To: stable@vger.kernel.org +Cc: Kumar Kartikeya Dwivedi , Alexei Starovoitov , Sasha Levin +Message-ID: <20260718215021.3447372-1-sashal@kernel.org> + +From: Kumar Kartikeya Dwivedi + +[ Upstream commit 47979314c0fe245ed54306e2f91b3f819c7c0f9f ] + +Convert all LPM trie usage of raw_spinlock to rqspinlock. + +Note that rcu_dereference_protected in trie_delete_elem is switched over +to plain rcu_dereference, the RCU read lock should be held from BPF +program side or eBPF syscall path, and the trie->lock is just acquired +before the dereference. It is not clear the reason the protected variant +was used from the commit history, but the above reasoning makes sense so +switch over. + +Closes: https://lore.kernel.org/lkml/000000000000adb08b061413919e@google.com +Signed-off-by: Kumar Kartikeya Dwivedi +Link: https://lore.kernel.org/r/20250316040541.108729-22-memxor@gmail.com +Signed-off-by: Alexei Starovoitov +Stable-dep-of: 2f884d371faf ("bpf: Allow LPM map access from sleepable BPF programs") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/lpm_trie.c | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +--- a/kernel/bpf/lpm_trie.c ++++ b/kernel/bpf/lpm_trie.c +@@ -349,8 +349,7 @@ static int trie_update_elem(struct bpf_m + */ + slot = &trie->root; + +- while ((node = rcu_dereference_protected(*slot, +- lockdep_is_held(&trie->lock)))) { ++ while ((node = rcu_dereference(*slot))) { + matchlen = longest_prefix_match(trie, node, key); + + if (node->prefixlen != matchlen || +@@ -475,8 +474,7 @@ static int trie_delete_elem(struct bpf_m + trim = &trie->root; + trim2 = trim; + parent = NULL; +- while ((node = rcu_dereference_protected( +- *trim, lockdep_is_held(&trie->lock)))) { ++ while ((node = rcu_dereference(*trim))) { + matchlen = longest_prefix_match(trie, node, key); + + if (node->prefixlen != matchlen || diff --git a/queue-5.15/bpf-reject-fragmented-frames-in-devmap.patch b/queue-5.15/bpf-reject-fragmented-frames-in-devmap.patch new file mode 100644 index 0000000000..41efad91af --- /dev/null +++ b/queue-5.15/bpf-reject-fragmented-frames-in-devmap.patch @@ -0,0 +1,70 @@ +From stable+bounces-277354-greg=kroah.com@vger.kernel.org Sat Jul 18 18:31:20 2026 +From: Sasha Levin +Date: Sat, 18 Jul 2026 12:31:12 -0400 +Subject: bpf: Reject fragmented frames in devmap +To: stable@vger.kernel.org +Cc: "Zhao Zhang" , stable@kernel.org, "Yuan Tan" , "Zhengchuan Liang" , "Xin Liu" , "Ren Wei" , "Emil Tsalapatis" , "Toke Høiland-Jørgensen" , "Alexei Starovoitov" , "Sasha Levin" +Message-ID: <20260718163112.3339166-2-sashal@kernel.org> + +From: Zhao Zhang + +[ Upstream commit aa496720618f1a6054f1c870bf10b4f6c99bf656 ] + +Devmap broadcast redirects clone the packet for all but the last +destination. + +For native XDP, that clone path copies only the linear xdp_frame data, +while fragmented frames keep skb_shared_info in tailroom outside the +linear area. Cloning such a frame leaves XDP_FLAGS_HAS_FRAGS set but +without valid frag metadata, and the later free path can interpret +uninitialized tail data as skb_shared_info, leading to an out-of-bounds +access during frame return. + +Reject fragmented native XDP frames in dev_map_enqueue_clone(). + +Add the same restriction to the generic XDP clone path in +dev_map_redirect_clone(). Generic XDP represents fragmented packets as +nonlinear skbs, and rejecting them here keeps clone-based broadcast +support aligned between native and generic XDP. + +Fixes: e624d4ed4aa8 ("xdp: Extend xdp_redirect_map with broadcast support") +Cc: stable@kernel.org +Reported-by: Yuan Tan +Reported-by: Zhengchuan Liang +Reported-by: Xin Liu +Assisted-by: Codex:GPT-5.4 +Signed-off-by: Zhao Zhang +Signed-off-by: Ren Wei +Reviewed-by: Emil Tsalapatis +Reviewed-by: Toke Høiland-Jørgensen +Link: https://lore.kernel.org/r/21c2d153dd25603d359069a02bf06779b51f6423.1780385378.git.zzhan461@ucr.edu +Signed-off-by: Alexei Starovoitov +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/devmap.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/kernel/bpf/devmap.c ++++ b/kernel/bpf/devmap.c +@@ -551,6 +551,10 @@ static int dev_map_enqueue_clone(struct + { + struct xdp_frame *nxdpf; + ++ /* Frags live outside the linear frame and cannot be cloned safely. */ ++ if (unlikely(xdp_frame_has_frags(xdpf))) ++ return -EOPNOTSUPP; ++ + nxdpf = xdpf_clone(xdpf); + if (!nxdpf) + return -ENOMEM; +@@ -708,6 +712,9 @@ static int dev_map_redirect_clone(struct + struct sk_buff *nskb; + int err; + ++ if (unlikely(skb_is_nonlinear(skb))) ++ return -EOPNOTSUPP; ++ + nskb = skb_clone(skb, GFP_ATOMIC); + if (!nskb) + return -ENOMEM; diff --git a/queue-5.15/btrfs-do-not-trim-a-device-which-is-not-writeable.patch b/queue-5.15/btrfs-do-not-trim-a-device-which-is-not-writeable.patch new file mode 100644 index 0000000000..b86fa7c3b8 --- /dev/null +++ b/queue-5.15/btrfs-do-not-trim-a-device-which-is-not-writeable.patch @@ -0,0 +1,144 @@ +From stable+bounces-278188-greg=kroah.com@vger.kernel.org Mon Jul 20 20:30:15 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 14:30:07 -0400 +Subject: btrfs: do not trim a device which is not writeable +To: stable@vger.kernel.org +Cc: Qu Wenruo , Su Yue , Filipe Manana , David Sterba , Sasha Levin +Message-ID: <20260720183007.3057972-1-sashal@kernel.org> + +From: Qu Wenruo + +[ Upstream commit 1b1937eb08f51319bf71575484cde2b8c517aedc ] + +[BUG] +There is a bug report that btrfs/242 can randomly fail with the +following NULL pointer dereference: + + run fstests btrfs/242 at 2026-06-01 10:25:08 + BTRFS: device fsid d4d7f234-487c-4787-88e4-47a8b68c9874 devid 1 transid 9 /dev/sdc (8:32) scanned by mount (122609) + BTRFS info (device sdc): first mount of filesystem d4d7f234-487c-4787-88e4-47a8b68c9874 + BTRFS info (device sdc): using crc32c checksum algorithm + BTRFS warning (device sdc): devid 2 uuid fbe72d72-3272-482d-80fb-ab88ed398192 is missing + BTRFS warning (device sdc): devid 2 uuid fbe72d72-3272-482d-80fb-ab88ed398192 is missing + BTRFS info (device sdc): allowing degraded mounts + BTRFS info (device sdc): turning on async discard + BTRFS info (device sdc): enabling free space tree + Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018 + user pgtable: 4k pages, 48-bit VAs, pgdp=000000013fd6b000 + CPU: 4 UID: 0 PID: 122625 Comm: fstrim Not tainted 7.0.10-2-default #1 PREEMPT(full) openSUSE Tumbleweed e9a5f6b24978fba3bf015a992f865837fdfff3dd + Hardware name: QEMU KVM Virtual Machine, BIOS edk2-20250812-19.fc42 08/12/2025 + pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) + pc : btrfs_trim_fs+0x34c/0xa00 [btrfs] + lr : btrfs_trim_fs+0x1f0/0xa00 [btrfs] + Call trace: + btrfs_trim_fs+0x34c/0xa00 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] (P) + btrfs_ioctl_fitrim+0xe8/0x178 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] + btrfs_ioctl+0xdd4/0x2bd8 [btrfs f02c1d570ceea621c69d302ba75dd61868083840] + __arm64_sys_ioctl+0xac/0x108 + invoke_syscall.constprop.0+0x5c/0xd0 + el0_svc_common.constprop.0+0x40/0xf0 + do_el0_svc+0x24/0x40 + el0_svc+0x40/0x1d0 + el0t_64_sync_handler+0xa0/0xe8 + el0t_64_sync+0x1b0/0x1b8 + Code: 17ffff83 f94017e0 f9002be0 f9402ea0 (f9400c00) + ---[ end trace 0000000000000000 ]--- + +Also the reporter is very kind to test the following ASSERT() added to +btrfs_trim_free_extents_throttle(): + + ASSERT(device->bdev, + "devid=%llu path=%s dev_state=0x%lx\n", + device->devid, btrfs_dev_name(device), device->dev_state); + +And it shows the following output: + + assertion failed: device->bdev, in extent-tree.c:6630 (devid=2 path=/dev/sdd dev_state=0x82) + +Which means the device->bdev is NULL, and the dev_state is +BTRFS_DEV_STATE_IN_FS_METADATA | BTRFS_DEV_STATE_ITEM_FOUND, without +BTRFS_DEV_STATE_WRITEABLE flag set. + +[CAUSE] +The pc points to the following call chain: + + btrfs_trim_fs() + |- btrfs_trim_free_extents() + |- btrfs_trim_free_extents_throttle() + |- bdev_max_discard_sectors(device->bdev) + +So the NULL pointer dereference is caused by device->bdev being NULL. + +This looks impossible by a quick glance, as just before calling +btrfs_trim_free_extents_throttle(), we have skipped any device that has +BTRFS_DEV_STATE_MISSING flag set. + +However in this particular case, there is a window where the missing +device is later re-scanned, causing btrfs to remove the +BTRFS_DEV_STATE_MISSING flag: + + btrfs_control_ioctl() + |- btrfs_scan_one_device() + |- device_list_add() + |- rcu_assign_pointer(device->name, name); + | This updates the missing device's path to the new good path. + | + |- clear_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state) + This removes the BTRFS_DEV_STATE_MISSING flag. + +This allows the missing device to re-appear and clear the +BTRFS_DEV_STATE_MISSING flag. However the device still does not have +the BTRFS_DEV_STATE_WRITEABLE flag set, nor is its bdev pointer updated. + +The bdev pointer remains NULL, triggering the crash later. + +[FIX] +This is a big de-synchronization between BTRFS_DEV_STATE_MISSING and +device->bdev pointer, and shows a gap in btrfs's re-appearing-device +handling. + +The proper handling of re-appearing device will need quite some extra +work, which is out of the context of this small fix. + +Thankfully the regular bbio submission path has already handled it well +by checking if the device->bdev is NULL before submitting. + +So here we just fix the crash by checking if the device is writeable and +has a bdev pointer before calling bdev_max_discard_sectors(). + +Reported-by: Su Yue +Link: https://lore.kernel.org/linux-btrfs/wlwir19t.fsf@damenly.org/ +Fixes: 499f377f49f0 ("btrfs: iterate over unused chunk space in FITRIM") +CC: stable@vger.kernel.org # 5.10+ +Reviewed-by: Filipe Manana +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/extent-tree.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +--- a/fs/btrfs/extent-tree.c ++++ b/fs/btrfs/extent-tree.c +@@ -5994,12 +5994,16 @@ static int btrfs_trim_free_extents(struc + + *trimmed = 0; + +- /* Discard not supported = nothing to do. */ +- if (!blk_queue_discard(bdev_get_queue(device->bdev))) ++ /* ++ * The caller only filters out MISSING devices, but a device that was ++ * missing at mount and later rescanned has MISSING cleared while bdev ++ * is still NULL and WRITEABLE is still unset. Skip those here. ++ */ ++ if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) || !device->bdev) + return 0; + +- /* Not writable = nothing to do. */ +- if (!test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) ++ /* Discard not supported = nothing to do. */ ++ if (!blk_queue_discard(bdev_get_queue(device->bdev))) + return 0; + + /* No free space = nothing to do. */ diff --git a/queue-5.15/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch b/queue-5.15/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch new file mode 100644 index 0000000000..cd50e7feb4 --- /dev/null +++ b/queue-5.15/btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch @@ -0,0 +1,198 @@ +From stable+bounces-278222-greg=kroah.com@vger.kernel.org Mon Jul 20 22:19:49 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:19:28 -0400 +Subject: btrfs: fix false IO failure after falling back to buffered write +To: stable@vger.kernel.org +Cc: Qu Wenruo , Boris Burkov , David Sterba , Sasha Levin +Message-ID: <20260720201928.3242739-1-sashal@kernel.org> + +From: Qu Wenruo + +[ 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 +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/inode.c | 34 ++++++++++++++++++++++++++-------- + fs/btrfs/ordered-data.c | 13 +++++++++++++ + fs/btrfs/ordered-data.h | 2 ++ + 3 files changed, 41 insertions(+), 8 deletions(-) + +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -8207,12 +8207,34 @@ static int btrfs_dio_iomap_end(struct in + if (submitted < length) { + pos += submitted; + length -= submitted; +- if (write) ++ if (write) { ++ struct btrfs_ordered_extent *ordered; ++ ++ /* ++ * We have a short write. Any range that was properly ++ * submitted will be finished through the bio ++ * completion path with an uptodate status matching ++ * the bio status. ++ * ++ * The remaining range was never submitted, so mark ++ * it as truncated and finish it without an error, so ++ * the extent is reclaimed without inserting a file ++ * extent item for it and without marking the mapping ++ * as failed, which would make a later buffered write ++ * fallback fail with a false -EIO. ++ */ ++ ordered = btrfs_lookup_ordered_extent(BTRFS_I(inode), ++ pos); ++ ASSERT(ordered); ++ btrfs_mark_ordered_extent_truncated(ordered, ++ pos - ordered->file_offset); ++ btrfs_put_ordered_extent(ordered); + __endio_write_update_ordered(BTRFS_I(inode), pos, +- length, false); +- else ++ length, true); ++ } else { + unlock_extent(&BTRFS_I(inode)->io_tree, pos, + pos + length - 1); ++ } + ret = -ENOTBLK; + } + +@@ -8839,11 +8861,7 @@ static void btrfs_invalidatepage(struct + EXTENT_LOCKED | EXTENT_DO_ACCOUNTING | + EXTENT_DEFRAG, 1, 0, &cached_state); + +- spin_lock_irq(&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); ++ btrfs_mark_ordered_extent_truncated(ordered, cur - ordered->file_offset); + + if (btrfs_dec_test_ordered_pending(inode, &ordered, + cur, range_end + 1 - cur)) { +--- a/fs/btrfs/ordered-data.c ++++ b/fs/btrfs/ordered-data.c +@@ -298,6 +298,19 @@ void btrfs_add_ordered_sum(struct btrfs_ + spin_unlock_irq(&tree->lock); + } + ++void btrfs_mark_ordered_extent_truncated(struct btrfs_ordered_extent *ordered, ++ u64 truncate_len) ++{ ++ struct btrfs_ordered_inode_tree *tree; ++ ++ tree = &BTRFS_I(ordered->inode)->ordered_tree; ++ ASSERT(truncate_len <= ordered->num_bytes); ++ spin_lock_irq(&tree->lock); ++ set_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags); ++ ordered->truncated_len = min(ordered->truncated_len, truncate_len); ++ spin_unlock_irq(&tree->lock); ++} ++ + /* + * Mark all ordered extents io inside the specified range finished. + * +--- a/fs/btrfs/ordered-data.h ++++ b/fs/btrfs/ordered-data.h +@@ -212,6 +212,8 @@ void btrfs_lock_and_flush_ordered_range( + struct extent_state **cached_state); + int btrfs_split_ordered_extent(struct btrfs_ordered_extent *ordered, u64 pre, + u64 post); ++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-5.15/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch b/queue-5.15/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch new file mode 100644 index 0000000000..213860db15 --- /dev/null +++ b/queue-5.15/btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch @@ -0,0 +1,197 @@ +From stable+bounces-278288-greg=kroah.com@vger.kernel.org Tue Jul 21 02:46:48 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 20:45:24 -0400 +Subject: btrfs: fix incorrect buffered IO fallback for append direct writes +To: stable@vger.kernel.org +Cc: Qu Wenruo , Boris Burkov , David Sterba , Sasha Levin +Message-ID: <20260721004524.3422340-2-sashal@kernel.org> + +From: Qu Wenruo + +[ 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 +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/inode.c | 43 ++++++++++++++++++++++++++++++++++++++++++- + 1 file changed, 42 insertions(+), 1 deletion(-) + +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -61,7 +61,9 @@ struct btrfs_iget_args { + + struct btrfs_dio_data { + ssize_t submitted; ++ loff_t old_isize; + struct extent_changeset *data_reserved; ++ bool updated_isize; + }; + + static const struct inode_operations btrfs_dir_inode_operations; +@@ -7904,6 +7906,7 @@ static int btrfs_get_blocks_direct_write + bool can_nocow = false; + bool space_reserved = false; + u64 prev_len; ++ loff_t old_isize; + int ret = 0; + + /* +@@ -7997,8 +8000,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); +@@ -8211,6 +8220,38 @@ static int btrfs_dio_iomap_end(struct in + struct btrfs_ordered_extent *ordered; + + /* ++ * 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. Any range that was properly + * submitted will be finished through the bio + * completion path with an uptodate status matching diff --git a/queue-5.15/cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch b/queue-5.15/cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch new file mode 100644 index 0000000000..245927736e --- /dev/null +++ b/queue-5.15/cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch @@ -0,0 +1,510 @@ +From stable+bounces-276751-greg=kroah.com@vger.kernel.org Thu Jul 16 18:52:01 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 11:36:43 -0400 +Subject: cifs: Add tracing for the cifs_tcon struct refcounting +To: stable@vger.kernel.org +Cc: David Howells , "Paulo Alcantara (Red Hat)" , Shyam Prasad N , linux-cifs@vger.kernel.org, linux-fsdevel@vger.kernel.org, Steve French , Sasha Levin +Message-ID: <20260716153644.582614-1-sashal@kernel.org> + +From: David Howells + +[ Upstream commit afc23febd51c7e24361e3a9c09f3e892eb0a41ea ] + +Add tracing for the refcounting/lifecycle of the cifs_tcon struct, marking +different events with different labels and giving each tcon its own debug +ID so that the tracelines corresponding to individual tcons can be +distinguished. This can be enabled with: + + echo 1 >/sys/kernel/debug/tracing/events/cifs/smb3_tcon_ref/enable + +Signed-off-by: David Howells +Acked-by: Paulo Alcantara (Red Hat) +cc: Shyam Prasad N +cc: linux-cifs@vger.kernel.org +cc: linux-fsdevel@vger.kernel.org +Signed-off-by: Steve French +Stable-dep-of: ec457f9afe5a ("smb: client: resolve SWN tcon from live registrations") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/cifs_swn.c | 13 +++--- + fs/cifs/cifsfs.c | 2 + + fs/cifs/cifsglob.h | 1 + fs/cifs/cifsproto.h | 9 +--- + fs/cifs/connect.c | 21 ++++++---- + fs/cifs/dfs_cache.c | 2 - + fs/cifs/misc.c | 10 +++-- + fs/cifs/smb2misc.c | 10 +++-- + fs/cifs/smb2ops.c | 7 +++ + fs/cifs/smb2pdu.c | 4 +- + fs/cifs/smb2transport.c | 2 + + fs/cifs/trace.h | 93 ++++++++++++++++++++++++++++++++++++++++++++++-- + 12 files changed, 141 insertions(+), 33 deletions(-) + +--- a/fs/cifs/cifs_swn.c ++++ b/fs/cifs/cifs_swn.c +@@ -313,17 +313,15 @@ static struct cifs_swn_reg *cifs_get_swn + reg = cifs_find_swn_reg(tcon); + if (!IS_ERR(reg)) { + kref_get(®->ref_count); +- mutex_unlock(&cifs_swnreg_idr_mutex); +- return reg; ++ goto unlock; + } else if (PTR_ERR(reg) != -EEXIST) { +- mutex_unlock(&cifs_swnreg_idr_mutex); +- return reg; ++ goto unlock; + } + + reg = kmalloc(sizeof(struct cifs_swn_reg), GFP_ATOMIC); + if (reg == NULL) { +- mutex_unlock(&cifs_swnreg_idr_mutex); +- return ERR_PTR(-ENOMEM); ++ ret = -ENOMEM; ++ goto fail_unlock; + } + + kref_init(®->ref_count); +@@ -354,7 +352,7 @@ static struct cifs_swn_reg *cifs_get_swn + reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); + + reg->tcon = tcon; +- ++unlock: + mutex_unlock(&cifs_swnreg_idr_mutex); + + return reg; +@@ -365,6 +363,7 @@ fail_idr: + idr_remove(&cifs_swnreg_idr, reg->id); + fail: + kfree(reg); ++fail_unlock: + mutex_unlock(&cifs_swnreg_idr_mutex); + return ERR_PTR(ret); + } +--- a/fs/cifs/cifsfs.c ++++ b/fs/cifs/cifsfs.c +@@ -705,6 +705,8 @@ static void cifs_umount_begin(struct sup + tcon = cifs_sb_master_tcon(cifs_sb); + + spin_lock(&cifs_tcp_ses_lock); ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_see_umount); + if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) { + /* we have other mounts to same share or we have + already tried to force umount this and woken up +--- a/fs/cifs/cifsglob.h ++++ b/fs/cifs/cifsglob.h +@@ -1025,6 +1025,7 @@ struct cached_fid { + */ + struct cifs_tcon { + struct list_head tcon_list; ++ int debug_id; /* Debugging for tracing */ + int tc_count; + struct list_head rlist; /* reconnect list */ + atomic_t num_local_opens; /* num of all opens including disconnected */ +--- a/fs/cifs/cifsproto.h ++++ b/fs/cifs/cifsproto.h +@@ -273,7 +273,7 @@ extern void cifs_close_deferred_file_und + extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx); + extern void cifs_put_tcp_session(struct TCP_Server_Info *server, + int from_reconnect); +-extern void cifs_put_tcon(struct cifs_tcon *tcon); ++extern void cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace); + + #if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) + extern void cifs_dfs_release_automount_timer(void); +@@ -489,8 +489,8 @@ extern int CIFSSMBLogoff(const unsigned + + extern struct cifs_ses *sesInfoAlloc(void); + extern void sesInfoFree(struct cifs_ses *); +-extern struct cifs_tcon *tconInfoAlloc(void); +-extern void tconInfoFree(struct cifs_tcon *); ++extern struct cifs_tcon *tconInfoAlloc(enum smb3_tcon_ref_trace trace); ++extern void tconInfoFree(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace); + + extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, + __u32 *pexpected_response_sequence_number); +@@ -634,9 +634,6 @@ static inline int cifs_create_options(st + return options; + } + +-struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); +-void cifs_put_tcon_super(struct super_block *sb); +- + static inline void cifs_mid_q_entry_release(struct mid_q_entry *midEntry) + { + kref_put(&midEntry->refcount, _cifs_mid_q_entry_release); +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -1663,7 +1663,7 @@ cifs_setup_ipc(struct cifs_ses *ses, str + } + } + +- tcon = tconInfoAlloc(); ++ tcon = tconInfoAlloc(netfs_trace_tcon_ref_new_ipc); + if (tcon == NULL) + return -ENOMEM; + +@@ -1678,7 +1678,7 @@ cifs_setup_ipc(struct cifs_ses *ses, str + + if (rc) { + cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc); +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail); + goto out; + } + +@@ -1709,7 +1709,7 @@ cifs_free_ipc(struct cifs_ses *ses) + if (tcon == NULL) + return 0; + +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc); + ses->tcon_ipc = NULL; + return 0; + } +@@ -2114,6 +2114,8 @@ cifs_find_tcon(struct cifs_ses *ses, str + if (!match_tcon(tcon, ctx)) + continue; + ++tcon->tc_count; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_find); + spin_unlock(&cifs_tcp_ses_lock); + return tcon; + } +@@ -2122,7 +2124,7 @@ cifs_find_tcon(struct cifs_ses *ses, str + } + + void +-cifs_put_tcon(struct cifs_tcon *tcon) ++cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace) + { + unsigned int xid; + struct cifs_ses *ses; +@@ -2137,6 +2139,7 @@ cifs_put_tcon(struct cifs_tcon *tcon) + ses = tcon->ses; + cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count); + spin_lock(&cifs_tcp_ses_lock); ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count - 1, trace); + if (--tcon->tc_count > 0) { + spin_unlock(&cifs_tcp_ses_lock); + return; +@@ -2164,7 +2167,7 @@ cifs_put_tcon(struct cifs_tcon *tcon) + _free_xid(xid); + + cifs_fscache_release_super_cookie(tcon); +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free); + cifs_put_smb_ses(ses); + } + +@@ -2212,7 +2215,7 @@ cifs_get_tcon(struct cifs_ses *ses, stru + goto out_fail; + } + +- tcon = tconInfoAlloc(); ++ tcon = tconInfoAlloc(netfs_trace_tcon_ref_new); + if (tcon == NULL) { + rc = -ENOMEM; + goto out_fail; +@@ -2388,7 +2391,7 @@ cifs_get_tcon(struct cifs_ses *ses, stru + return tcon; + + out_fail: +- tconInfoFree(tcon); ++ tconInfoFree(tcon, netfs_trace_tcon_ref_free_fail); + return ERR_PTR(rc); + } + +@@ -2405,7 +2408,7 @@ cifs_put_tlink(struct tcon_link *tlink) + } + + if (!IS_ERR(tlink_tcon(tlink))) +- cifs_put_tcon(tlink_tcon(tlink)); ++ cifs_put_tcon(tlink_tcon(tlink), netfs_trace_tcon_ref_put_tlink); + kfree(tlink); + return; + } +@@ -2959,7 +2962,7 @@ static inline void mount_put_conns(struc + int rc = 0; + + if (mnt_ctx->tcon) +- cifs_put_tcon(mnt_ctx->tcon); ++ cifs_put_tcon(mnt_ctx->tcon, netfs_trace_tcon_ref_put_mnt_ctx); + else if (mnt_ctx->ses) + cifs_put_smb_ses(mnt_ctx->ses); + else if (mnt_ctx->server) +--- a/fs/cifs/dfs_cache.c ++++ b/fs/cifs/dfs_cache.c +@@ -1544,7 +1544,7 @@ static void refresh_mounts(struct cifs_s + mutex_unlock(&server->refpath_lock); + + __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false); +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_dfs_refer); + } + } + +--- a/fs/cifs/misc.c ++++ b/fs/cifs/misc.c +@@ -101,9 +101,10 @@ sesInfoFree(struct cifs_ses *buf_to_free + } + + struct cifs_tcon * +-tconInfoAlloc(void) ++tconInfoAlloc(enum smb3_tcon_ref_trace trace) + { + struct cifs_tcon *ret_buf; ++ static atomic_t tcon_debug_id; + + ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL); + if (!ret_buf) +@@ -116,7 +117,8 @@ tconInfoAlloc(void) + + atomic_inc(&tconInfoAllocCount); + ret_buf->tidStatus = CifsNew; +- ++ret_buf->tc_count; ++ ret_buf->debug_id = atomic_inc_return(&tcon_debug_id); ++ ret_buf->tc_count = 1; + INIT_LIST_HEAD(&ret_buf->openFileList); + INIT_LIST_HEAD(&ret_buf->tcon_list); + spin_lock_init(&ret_buf->open_file_lock); +@@ -124,17 +126,19 @@ tconInfoAlloc(void) + spin_lock_init(&ret_buf->stat_lock); + atomic_set(&ret_buf->num_local_opens, 0); + atomic_set(&ret_buf->num_remote_opens, 0); ++ trace_smb3_tcon_ref(ret_buf->debug_id, ret_buf->tc_count, trace); + + return ret_buf; + } + + void +-tconInfoFree(struct cifs_tcon *buf_to_free) ++tconInfoFree(struct cifs_tcon *buf_to_free, enum smb3_tcon_ref_trace trace) + { + if (buf_to_free == NULL) { + cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n"); + return; + } ++ trace_smb3_tcon_ref(buf_to_free->debug_id, buf_to_free->tc_count, trace); + atomic_dec(&tconInfoAllocCount); + kfree(buf_to_free->nativeFileSystem); + kfree_sensitive(buf_to_free->password); +--- a/fs/cifs/smb2misc.c ++++ b/fs/cifs/smb2misc.c +@@ -773,7 +773,7 @@ smb2_cancelled_close_fid(struct work_str + if (rc) + cifs_tcon_dbg(VFS, "Close cancelled mid failed rc:%d\n", rc); + +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close_fid); + kfree(cancelled); + } + +@@ -817,6 +817,8 @@ smb2_handle_cancelled_close(struct cifs_ + if (tcon->tc_count <= 0) { + struct TCP_Server_Info *server = NULL; + ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_see_cancelled_close); + WARN_ONCE(tcon->tc_count < 0, "tcon refcount is negative"); + spin_unlock(&cifs_tcp_ses_lock); + +@@ -830,12 +832,14 @@ smb2_handle_cancelled_close(struct cifs_ + return 0; + } + tcon->tc_count++; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_cancelled_close); + spin_unlock(&cifs_tcp_ses_lock); + + rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0, + persistent_fid, volatile_fid); + if (rc) +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close); + + return rc; + } +@@ -863,7 +867,7 @@ smb2_handle_cancelled_mid(struct mid_q_e + rsp->PersistentFileId, + rsp->VolatileFileId); + if (rc) +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid); + + return rc; + } +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -2895,8 +2895,11 @@ smb2_get_dfs_refer(const unsigned int xi + tcon = list_first_entry_or_null(&ses->tcon_list, + struct cifs_tcon, + tcon_list); +- if (tcon) ++ if (tcon) { + tcon->tc_count++; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_dfs_refer); ++ } + spin_unlock(&cifs_tcp_ses_lock); + } + +@@ -2960,6 +2963,8 @@ smb2_get_dfs_refer(const unsigned int xi + /* ipc tcons are not refcounted */ + spin_lock(&cifs_tcp_ses_lock); + tcon->tc_count--; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_dec_dfs_refer); + /* tc_count can never go negative */ + WARN_ON(tcon->tc_count < 0); + spin_unlock(&cifs_tcp_ses_lock); +--- a/fs/cifs/smb2pdu.c ++++ b/fs/cifs/smb2pdu.c +@@ -3769,6 +3769,8 @@ void smb2_reconnect_server(struct work_s + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { + if (tcon->need_reconnect || tcon->need_reopen_files) { + tcon->tc_count++; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_reconnect_server); + list_add_tail(&tcon->rlist, &tmp_list); + tcon_exist = true; + } +@@ -3802,7 +3804,7 @@ void smb2_reconnect_server(struct work_s + if (tcon->ipc) + cifs_put_smb_ses(tcon->ses); + else +- cifs_put_tcon(tcon); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_reconnect_server); + } + + cifs_dbg(FYI, "Reconnecting tcons finished\n"); +--- a/fs/cifs/smb2transport.c ++++ b/fs/cifs/smb2transport.c +@@ -168,6 +168,8 @@ smb2_find_smb_sess_tcon_unlocked(struct + if (tcon->tid != tid) + continue; + ++tcon->tc_count; ++ trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, ++ netfs_trace_tcon_ref_get_find_sess_tcon); + return tcon; + } + +--- a/fs/cifs/trace.h ++++ b/fs/cifs/trace.h +@@ -3,6 +3,9 @@ + * Copyright (C) 2018, Microsoft Corporation. + * + * Author(s): Steve French ++ * ++ * Please use this 3-part article as a reference for writing new tracepoints: ++ * https://lwn.net/Articles/379903/ + */ + #undef TRACE_SYSTEM + #define TRACE_SYSTEM cifs +@@ -13,9 +16,71 @@ + #include + + /* +- * Please use this 3-part article as a reference for writing new tracepoints: +- * https://lwn.net/Articles/379903/ ++ * Specify enums for tracing information. ++ */ ++#define smb3_tcon_ref_traces \ ++ EM(netfs_trace_tcon_ref_dec_dfs_refer, "DEC DfsRef") \ ++ EM(netfs_trace_tcon_ref_free, "FRE ") \ ++ EM(netfs_trace_tcon_ref_free_fail, "FRE Fail ") \ ++ EM(netfs_trace_tcon_ref_free_ipc, "FRE Ipc ") \ ++ EM(netfs_trace_tcon_ref_free_ipc_fail, "FRE Ipc-F ") \ ++ EM(netfs_trace_tcon_ref_free_reconnect_server, "FRE Reconn") \ ++ EM(netfs_trace_tcon_ref_get_cancelled_close, "GET Cn-Cls") \ ++ EM(netfs_trace_tcon_ref_get_dfs_refer, "GET DfsRef") \ ++ EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ ++ EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \ ++ EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \ ++ EM(netfs_trace_tcon_ref_new, "NEW ") \ ++ EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \ ++ EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \ ++ EM(netfs_trace_tcon_ref_put_cancelled_close, "PUT Cn-Cls") \ ++ EM(netfs_trace_tcon_ref_put_cancelled_close_fid, "PUT Cn-Fid") \ ++ EM(netfs_trace_tcon_ref_put_cancelled_mid, "PUT Cn-Mid") \ ++ EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \ ++ EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \ ++ EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \ ++ EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \ ++ EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \ ++ EM(netfs_trace_tcon_ref_see_fscache_collision, "SEE FV-CO!") \ ++ EM(netfs_trace_tcon_ref_see_fscache_okay, "SEE FV-Ok ") \ ++ EM(netfs_trace_tcon_ref_see_fscache_relinq, "SEE FV-Rlq") \ ++ E_(netfs_trace_tcon_ref_see_umount, "SEE Umount") ++ ++#undef EM ++#undef E_ ++ ++/* ++ * Define those tracing enums. + */ ++#ifndef __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY ++#define __SMB3_DECLARE_TRACE_ENUMS_ONCE_ONLY ++ ++#define EM(a, b) a, ++#define E_(a, b) a ++ ++enum smb3_tcon_ref_trace { smb3_tcon_ref_traces } __mode(byte); ++ ++#undef EM ++#undef E_ ++#endif ++ ++/* ++ * Export enum symbols via userspace. ++ */ ++#define EM(a, b) TRACE_DEFINE_ENUM(a); ++#define E_(a, b) TRACE_DEFINE_ENUM(a); ++ ++smb3_tcon_ref_traces; ++ ++#undef EM ++#undef E_ ++ ++/* ++ * Now redefine the EM() and E_() macros to map the enums to the strings that ++ * will be printed in the output. ++ */ ++#define EM(a, b) { a, b }, ++#define E_(a, b) { a, b } + + /* For logging errors in read or write */ + DECLARE_EVENT_CLASS(smb3_rw_err_class, +@@ -937,6 +1002,30 @@ DEFINE_SMB3_CREDIT_EVENT(too_many_credit + DEFINE_SMB3_CREDIT_EVENT(add_credits); + DEFINE_SMB3_CREDIT_EVENT(set_credits); + ++ ++TRACE_EVENT(smb3_tcon_ref, ++ TP_PROTO(unsigned int tcon_debug_id, int ref, ++ enum smb3_tcon_ref_trace trace), ++ TP_ARGS(tcon_debug_id, ref, trace), ++ TP_STRUCT__entry( ++ __field(unsigned int, tcon) ++ __field(int, ref) ++ __field(enum smb3_tcon_ref_trace, trace) ++ ), ++ TP_fast_assign( ++ __entry->tcon = tcon_debug_id; ++ __entry->ref = ref; ++ __entry->trace = trace; ++ ), ++ TP_printk("TC=%08x %s r=%u", ++ __entry->tcon, ++ __print_symbolic(__entry->trace, smb3_tcon_ref_traces), ++ __entry->ref) ++ ); ++ ++ ++#undef EM ++#undef E_ + #endif /* _CIFS_TRACE_H */ + + #undef TRACE_INCLUDE_PATH diff --git a/queue-5.15/cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch b/queue-5.15/cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch new file mode 100644 index 0000000000..8cf6c8e6bb --- /dev/null +++ b/queue-5.15/cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch @@ -0,0 +1,2056 @@ +From stable+bounces-274536-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:12 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 16:02:11 -0400 +Subject: cifs: Create a new shared file holding smb2 pdu definitions +To: stable@vger.kernel.org +Cc: Ronnie Sahlberg , Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260714200215.3152449-1-sashal@kernel.org> + +From: Ronnie Sahlberg + +[ Upstream commit 0d35e382e4e96a4fd97a1438bc1b11a91d2d85a6 ] + +This file will contain all the definitions we need for SMB2 packets +and will follow the naming convention of MS-SMB2.PDF as closely +as possible to make it easier to cross-reference beween the definitions +and the standard. + +The content of this file will mostly consist of migration of existing +definitions in the cifs/smb2.pdu.h and ksmbd/smb2pdu.h files +with some additional tweaks as the two files have diverged. + +This patch introduces the new smbfs_common/smb2pdu.h file +and migrates the SMB2 header as well as TREE_CONNECT and TREE_DISCONNECT +to the shared file. + +Signed-off-by: Ronnie Sahlberg +Reviewed-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/cifsfs.c | 1 + fs/cifs/cifsglob.h | 3 + fs/cifs/connect.c | 4 + fs/cifs/misc.c | 2 + fs/cifs/smb2maperror.c | 16 + + fs/cifs/smb2misc.c | 43 ++--- + fs/cifs/smb2ops.c | 65 ++++---- + fs/cifs/smb2pdu.c | 106 ++++++------- + fs/cifs/smb2pdu.h | 373 ++++------------------------------------------ + fs/cifs/smb2proto.h | 2 + fs/cifs/smb2transport.c | 36 ++-- + fs/smbfs_common/smb2pdu.h | 318 +++++++++++++++++++++++++++++++++++++++ + 12 files changed, 493 insertions(+), 476 deletions(-) + create mode 100644 fs/smbfs_common/smb2pdu.h + +--- a/fs/cifs/cifsfs.c ++++ b/fs/cifs/cifsfs.c +@@ -38,7 +38,6 @@ + #include + #include "cifs_spnego.h" + #include "fscache.h" +-#include "smb2pdu.h" + #ifdef CONFIG_CIFS_DFS_UPCALL + #include "dfs_cache.h" + #endif +--- a/fs/cifs/cifsglob.h ++++ b/fs/cifs/cifsglob.h +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include "../smbfs_common/smb2pdu.h" + #include "smb2pdu.h" + #include "smb2glob.h" + +@@ -794,7 +795,7 @@ revert_current_mid(struct TCP_Server_Inf + + static inline void + revert_current_mid_from_hdr(struct TCP_Server_Info *server, +- const struct smb2_sync_hdr *shdr) ++ const struct smb2_hdr *shdr) + { + unsigned int num = le16_to_cpu(shdr->CreditCharge); + +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -722,7 +722,7 @@ dequeue_mid(struct mid_q_entry *mid, boo + static unsigned int + smb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) + { +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; + + /* + * SMB1 does not use credits. +@@ -926,7 +926,7 @@ cifs_handle_standard(struct TCP_Server_I + static void + smb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server) + { +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buffer; + int scredits, in_flight; + + /* +--- a/fs/cifs/misc.c ++++ b/fs/cifs/misc.c +@@ -150,7 +150,7 @@ cifs_buf_get(void) + * SMB2 header is bigger than CIFS one - no problems to clean some + * more bytes for CIFS. + */ +- size_t buf_size = sizeof(struct smb2_sync_hdr); ++ size_t buf_size = sizeof(struct smb2_hdr); + + /* + * We could use negotiated size instead of max_msgsize - +--- a/fs/cifs/smb2maperror.c ++++ b/fs/cifs/smb2maperror.c +@@ -2439,14 +2439,16 @@ smb2_print_status(__le32 status) + int + map_smb2_to_linux_error(char *buf, bool log_err) + { +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + unsigned int i; + int rc = -EIO; + __le32 smb2err = shdr->Status; + + if (smb2err == 0) { +- trace_smb3_cmd_done(shdr->TreeId, shdr->SessionId, +- le16_to_cpu(shdr->Command), le64_to_cpu(shdr->MessageId)); ++ trace_smb3_cmd_done(le32_to_cpu(shdr->Id.SyncId.TreeId), ++ le64_to_cpu(shdr->SessionId), ++ le16_to_cpu(shdr->Command), ++ le64_to_cpu(shdr->MessageId)); + return 0; + } + +@@ -2470,8 +2472,10 @@ map_smb2_to_linux_error(char *buf, bool + cifs_dbg(FYI, "Mapping SMB2 status code 0x%08x to POSIX err %d\n", + __le32_to_cpu(smb2err), rc); + +- trace_smb3_cmd_err(shdr->TreeId, shdr->SessionId, +- le16_to_cpu(shdr->Command), +- le64_to_cpu(shdr->MessageId), le32_to_cpu(smb2err), rc); ++ trace_smb3_cmd_err(le32_to_cpu(shdr->Id.SyncId.TreeId), ++ le64_to_cpu(shdr->SessionId), ++ le16_to_cpu(shdr->Command), ++ le64_to_cpu(shdr->MessageId), ++ le32_to_cpu(smb2err), rc); + return rc; + } +--- a/fs/cifs/smb2misc.c ++++ b/fs/cifs/smb2misc.c +@@ -8,7 +8,6 @@ + * + */ + #include +-#include "smb2pdu.h" + #include "cifsglob.h" + #include "cifsproto.h" + #include "smb2proto.h" +@@ -19,7 +18,7 @@ + #include "nterr.h" + + static int +-check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid) ++check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) + { + __u64 wire_mid = le64_to_cpu(shdr->MessageId); + +@@ -81,9 +80,9 @@ static const __le16 smb2_rsp_struct_size + /* SMB2_OPLOCK_BREAK */ cpu_to_le16(24) + }; + +-#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_sync_hdr) + sizeof(struct smb2_negotiate_rsp)) ++#define SMB311_NEGPROT_BASE_SIZE (sizeof(struct smb2_hdr) + sizeof(struct smb2_negotiate_rsp)) + +-static __u32 get_neg_ctxt_len(struct smb2_sync_hdr *hdr, __u32 len, ++static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len, + __u32 non_ctxlen) + { + __u16 neg_count; +@@ -135,13 +134,13 @@ static __u32 get_neg_ctxt_len(struct smb + int + smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr) + { +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; +- struct smb2_sync_pdu *pdu = (struct smb2_sync_pdu *)shdr; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; ++ struct smb2_pdu *pdu = (struct smb2_pdu *)shdr; + __u64 mid; + __u32 clc_len; /* calculated length */ + int command; +- int pdu_size = sizeof(struct smb2_sync_pdu); +- int hdr_size = sizeof(struct smb2_sync_hdr); ++ int pdu_size = sizeof(struct smb2_pdu); ++ int hdr_size = sizeof(struct smb2_hdr); + + /* + * Add function to do table lookup of StructureSize by command +@@ -155,7 +154,7 @@ smb2_check_message(char *buf, unsigned i + /* decrypt frame now that it is completely read in */ + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(ses, &srvr->smb_ses_list, smb_ses_list) { +- if (ses->Suid == thdr->SessionId) ++ if (ses->Suid == le64_to_cpu(thdr->SessionId)) + break; + } + spin_unlock(&cifs_tcp_ses_lock); +@@ -296,7 +295,7 @@ static const bool has_smb2_data_area[NUM + * area and the offset to it (from the beginning of the smb are also returned. + */ + char * +-smb2_get_data_area_len(int *off, int *len, struct smb2_sync_hdr *shdr) ++smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *shdr) + { + const int max_off = 4096; + const int max_len = 128 * 1024; +@@ -395,8 +394,8 @@ smb2_get_data_area_len(int *off, int *le + unsigned int + smb2_calc_size(void *buf, struct TCP_Server_Info *srvr) + { +- struct smb2_sync_pdu *pdu = (struct smb2_sync_pdu *)buf; +- struct smb2_sync_hdr *shdr = &pdu->sync_hdr; ++ struct smb2_pdu *pdu = (struct smb2_pdu *)buf; ++ struct smb2_hdr *shdr = &pdu->hdr; + int offset; /* the offset from the beginning of SMB to data area */ + int data_length; /* the length of the variable length data area */ + /* Structure Size has already been checked to make sure it is 64 */ +@@ -663,7 +662,7 @@ smb2_is_valid_oplock_break(char *buffer, + + cifs_dbg(FYI, "Checking for oplock break\n"); + +- if (rsp->sync_hdr.Command != SMB2_OPLOCK_BREAK) ++ if (rsp->hdr.Command != SMB2_OPLOCK_BREAK) + return false; + + if (rsp->StructureSize != +@@ -813,23 +812,23 @@ smb2_handle_cancelled_close(struct cifs_ + int + smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server) + { +- struct smb2_sync_hdr *sync_hdr = mid->resp_buf; ++ struct smb2_hdr *hdr = mid->resp_buf; + struct smb2_create_rsp *rsp = mid->resp_buf; + struct cifs_tcon *tcon; + int rc; + +- if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || sync_hdr->Command != SMB2_CREATE || +- sync_hdr->Status != STATUS_SUCCESS) ++ if ((mid->optype & CIFS_CP_CREATE_CLOSE_OP) || hdr->Command != SMB2_CREATE || ++ hdr->Status != STATUS_SUCCESS) + return 0; + +- tcon = smb2_find_smb_tcon(server, sync_hdr->SessionId, +- sync_hdr->TreeId); ++ tcon = smb2_find_smb_tcon(server, le64_to_cpu(hdr->SessionId), ++ le32_to_cpu(hdr->Id.SyncId.TreeId)); + if (!tcon) + return -ENOENT; + + rc = __smb2_handle_cancelled_cmd(tcon, +- le16_to_cpu(sync_hdr->Command), +- le64_to_cpu(sync_hdr->MessageId), ++ le16_to_cpu(hdr->Command), ++ le64_to_cpu(hdr->MessageId), + rsp->PersistentFileId, + rsp->VolatileFileId); + if (rc) +@@ -853,10 +852,10 @@ smb311_update_preauth_hash(struct cifs_s + { + int i, rc; + struct sdesc *d; +- struct smb2_sync_hdr *hdr; ++ struct smb2_hdr *hdr; + struct TCP_Server_Info *server = cifs_ses_server(ses); + +- hdr = (struct smb2_sync_hdr *)iov[0].iov_base; ++ hdr = (struct smb2_hdr *)iov[0].iov_base; + /* neg prot are always taken */ + if (hdr->Command == SMB2_NEGOTIATE) + goto ok; +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -326,7 +326,7 @@ static struct mid_q_entry * + __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue) + { + struct mid_q_entry *mid; +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + __u64 wire_mid = le64_to_cpu(shdr->MessageId); + + if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) { +@@ -368,11 +368,11 @@ static void + smb2_dump_detail(void *buf, struct TCP_Server_Info *server) + { + #ifdef CONFIG_CIFS_DEBUG2 +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + cifs_server_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n", + shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, +- shdr->ProcessId); ++ shdr->Id.SyncId.ProcessId); + cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, + server->ops->calc_smb_size(buf, server)); + #endif +@@ -890,7 +890,7 @@ int open_cached_dir(unsigned int xid, st + oparms.fid->persistent_fid = o_rsp->PersistentFileId; + oparms.fid->volatile_fid = o_rsp->VolatileFileId; + #ifdef CONFIG_CIFS_DEBUG2 +- oparms.fid->mid = le64_to_cpu(o_rsp->sync_hdr.MessageId); ++ oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId); + #endif /* CIFS_DEBUG2 */ + + tcon->crfid.tcon = tcon; +@@ -2430,7 +2430,7 @@ again: + + /* If the open failed there is nothing to do */ + op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base; +- if (op_rsp == NULL || op_rsp->sync_hdr.Status != STATUS_SUCCESS) { ++ if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) { + cifs_dbg(FYI, "query_dir_first: open failed rc=%d\n", rc); + goto qdf_free; + } +@@ -2449,7 +2449,7 @@ again: + atomic_inc(&tcon->num_remote_opens); + + qd_rsp = (struct smb2_query_directory_rsp *)rsp_iov[1].iov_base; +- if (qd_rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) { ++ if (qd_rsp->hdr.Status == STATUS_NO_MORE_FILES) { + trace_smb3_query_dir_done(xid, fid->persistent_fid, + tcon->tid, tcon->ses->Suid, 0, 0); + srch_inf->endOfSearch = true; +@@ -2501,7 +2501,7 @@ smb2_close_dir(const unsigned int xid, s + static bool + smb2_is_status_pending(char *buf, struct TCP_Server_Info *server) + { +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + int scredits, in_flight; + + if (shdr->Status != STATUS_PENDING) +@@ -2528,13 +2528,14 @@ smb2_is_status_pending(char *buf, struct + static bool + smb2_is_session_expired(char *buf) + { +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + if (shdr->Status != STATUS_NETWORK_SESSION_EXPIRED && + shdr->Status != STATUS_USER_SESSION_DELETED) + return false; + +- trace_smb3_ses_expired(shdr->TreeId, shdr->SessionId, ++ trace_smb3_ses_expired(le32_to_cpu(shdr->Id.SyncId.TreeId), ++ le64_to_cpu(shdr->SessionId), + le16_to_cpu(shdr->Command), + le64_to_cpu(shdr->MessageId)); + cifs_dbg(FYI, "Session expired or deleted\n"); +@@ -2545,7 +2546,7 @@ smb2_is_session_expired(char *buf) + static bool + smb2_is_status_io_timeout(char *buf) + { +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + + if (shdr->Status == STATUS_IO_TIMEOUT) + return true; +@@ -2556,7 +2557,7 @@ smb2_is_status_io_timeout(char *buf) + static void + smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server) + { +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct list_head *tmp, *tmp1; + struct cifs_ses *ses; + struct cifs_tcon *tcon; +@@ -2569,7 +2570,7 @@ smb2_is_network_name_deleted(char *buf, + ses = list_entry(tmp, struct cifs_ses, smb_ses_list); + list_for_each(tmp1, &ses->tcon_list) { + tcon = list_entry(tmp1, struct cifs_tcon, tcon_list); +- if (tcon->tid == shdr->TreeId) { ++ if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) { + tcon->need_reconnect = true; + spin_unlock(&cifs_tcp_ses_lock); + pr_warn_once("Server share %s deleted.\n", +@@ -2596,9 +2597,9 @@ smb2_oplock_response(struct cifs_tcon *t + void + smb2_set_related(struct smb_rqst *rqst) + { +- struct smb2_sync_hdr *shdr; ++ struct smb2_hdr *shdr; + +- shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base); ++ shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); + if (shdr == NULL) { + cifs_dbg(FYI, "shdr NULL in smb2_set_related\n"); + return; +@@ -2611,13 +2612,13 @@ char smb2_padding[7] = {0, 0, 0, 0, 0, 0 + void + smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst) + { +- struct smb2_sync_hdr *shdr; ++ struct smb2_hdr *shdr; + struct cifs_ses *ses = tcon->ses; + struct TCP_Server_Info *server = ses->server; + unsigned long len = smb_rqst_len(server, rqst); + int i, num_padding; + +- shdr = (struct smb2_sync_hdr *)(rqst->rq_iov[0].iov_base); ++ shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base); + if (shdr == NULL) { + cifs_dbg(FYI, "shdr NULL in smb2_set_next_command\n"); + return; +@@ -3172,7 +3173,7 @@ smb2_query_symlink(const unsigned int xi + resp_buftype, rsp_iov); + + create_rsp = rsp_iov[0].iov_base; +- if (create_rsp && create_rsp->sync_hdr.Status) ++ if (create_rsp && create_rsp->hdr.Status) + err_iov = rsp_iov[0]; + ioctl_rsp = rsp_iov[1].iov_base; + +@@ -4423,8 +4424,8 @@ static void + fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len, + struct smb_rqst *old_rq, __le16 cipher_type) + { +- struct smb2_sync_hdr *shdr = +- (struct smb2_sync_hdr *)old_rq->rq_iov[0].iov_base; ++ struct smb2_hdr *shdr = ++ (struct smb2_hdr *)old_rq->rq_iov[0].iov_base; + + memset(tr_hdr, 0, sizeof(struct smb2_transform_hdr)); + tr_hdr->ProtocolId = SMB2_TRANSFORM_PROTO_NUM; +@@ -4562,7 +4563,7 @@ crypt_message(struct TCP_Server_Info *se + unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + void *creq; + +- rc = smb2_get_enc_key(server, tr_hdr->SessionId, enc, key); ++ rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not get %scryption key\n", __func__, + enc ? "en" : "de"); +@@ -4848,7 +4849,7 @@ handle_read_data(struct TCP_Server_Info + unsigned int cur_page_idx; + unsigned int pad_len; + struct cifs_readdata *rdata = mid->callback_data; +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct bio_vec *bvec = NULL; + struct iov_iter iter; + struct kvec iov; +@@ -5177,7 +5178,7 @@ receive_encrypted_standard(struct TCP_Se + { + int ret, length; + char *buf = server->smallbuf; +- struct smb2_sync_hdr *shdr; ++ struct smb2_hdr *shdr; + unsigned int pdu_length = server->pdu_size; + unsigned int buf_size; + unsigned int next_cmd; +@@ -5208,7 +5209,7 @@ receive_encrypted_standard(struct TCP_Se + + next_is_large = server->large_buf; + one_more: +- shdr = (struct smb2_sync_hdr *)buf; ++ shdr = (struct smb2_hdr *)buf; + next_cmd = le32_to_cpu(shdr->NextCommand); + if (next_cmd) { + if (WARN_ON_ONCE(next_cmd > pdu_length)) +@@ -5279,7 +5280,7 @@ smb3_receive_transform(struct TCP_Server + unsigned int orig_len = le32_to_cpu(tr_hdr->OriginalMessageSize); + + if (pdu_length < sizeof(struct smb2_transform_hdr) + +- sizeof(struct smb2_sync_hdr)) { ++ sizeof(struct smb2_hdr)) { + cifs_server_dbg(VFS, "Transform message is too small (%u)\n", + pdu_length); + cifs_reconnect(server); +@@ -5312,7 +5313,7 @@ smb3_handle_read_data(struct TCP_Server_ + static int + smb2_next_header(char *buf) + { +- struct smb2_sync_hdr *hdr = (struct smb2_sync_hdr *)buf; ++ struct smb2_hdr *hdr = (struct smb2_hdr *)buf; + struct smb2_transform_hdr *t_hdr = (struct smb2_transform_hdr *)buf; + + if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) +@@ -5856,7 +5857,7 @@ struct smb_version_values smb20_values = + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +- .header_size = sizeof(struct smb2_sync_hdr), ++ .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp), +@@ -5878,7 +5879,7 @@ struct smb_version_values smb21_values = + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +- .header_size = sizeof(struct smb2_sync_hdr), ++ .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp), +@@ -5899,7 +5900,7 @@ struct smb_version_values smb3any_values + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +- .header_size = sizeof(struct smb2_sync_hdr), ++ .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp), +@@ -5920,7 +5921,7 @@ struct smb_version_values smbdefault_val + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +- .header_size = sizeof(struct smb2_sync_hdr), ++ .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp), +@@ -5941,7 +5942,7 @@ struct smb_version_values smb30_values = + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +- .header_size = sizeof(struct smb2_sync_hdr), ++ .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp), +@@ -5962,7 +5963,7 @@ struct smb_version_values smb302_values + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +- .header_size = sizeof(struct smb2_sync_hdr), ++ .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp), +@@ -5983,7 +5984,7 @@ struct smb_version_values smb311_values + .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK, + .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK, + .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK, +- .header_size = sizeof(struct smb2_sync_hdr), ++ .header_size = sizeof(struct smb2_hdr), + .header_preamble_size = 0, + .max_header_size = MAX_SMB2_HDR_SIZE, + .read_rsp_size = sizeof(struct smb2_read_rsp), +--- a/fs/cifs/smb2pdu.c ++++ b/fs/cifs/smb2pdu.c +@@ -23,7 +23,6 @@ + #include + #include + #include +-#include "smb2pdu.h" + #include "cifsglob.h" + #include "cifsacl.h" + #include "cifsproto.h" +@@ -84,7 +83,7 @@ int smb3_encryption_required(const struc + } + + static void +-smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd, ++smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, + const struct cifs_tcon *tcon, + struct TCP_Server_Info *server) + { +@@ -104,7 +103,7 @@ smb2_hdr_assemble(struct smb2_sync_hdr * + } else { + shdr->CreditRequest = cpu_to_le16(2); + } +- shdr->ProcessId = cpu_to_le32((__u16)current->tgid); ++ shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid); + + if (!tcon) + goto out; +@@ -115,10 +114,10 @@ smb2_hdr_assemble(struct smb2_sync_hdr * + shdr->CreditCharge = cpu_to_le16(1); + /* else CreditCharge MBZ */ + +- shdr->TreeId = tcon->tid; ++ shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid); + /* Uid is not converted */ + if (tcon->ses) +- shdr->SessionId = tcon->ses->Suid; ++ shdr->SessionId = cpu_to_le64(tcon->ses->Suid); + + /* + * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have +@@ -338,7 +337,7 @@ fill_small_buf(__le16 smb2_command, stru + void *buf, + unsigned int *total_len) + { +- struct smb2_sync_pdu *spdu = (struct smb2_sync_pdu *)buf; ++ struct smb2_pdu *spdu = (struct smb2_pdu *)buf; + /* lookup word count ie StructureSize from table */ + __u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)]; + +@@ -348,10 +347,10 @@ fill_small_buf(__le16 smb2_command, stru + */ + memset(buf, 0, 256); + +- smb2_hdr_assemble(&spdu->sync_hdr, smb2_command, tcon, server); ++ smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server); + spdu->StructureSize2 = cpu_to_le16(parmsize); + +- *total_len = parmsize + sizeof(struct smb2_sync_hdr); ++ *total_len = parmsize + sizeof(struct smb2_hdr); + } + + /* +@@ -379,7 +378,7 @@ static int __smb2_plain_req_init(__le16 + } + + fill_small_buf(smb2_command, tcon, server, +- (struct smb2_sync_hdr *)(*request_buf), ++ (struct smb2_hdr *)(*request_buf), + total_len); + + if (tcon != NULL) { +@@ -869,7 +868,7 @@ SMB2_negotiate(const unsigned int xid, s + if (rc) + return rc; + +- req->sync_hdr.SessionId = 0; ++ req->hdr.SessionId = 0; + + memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); + memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE); +@@ -1034,7 +1033,7 @@ SMB2_negotiate(const unsigned int xid, s + server->cipher_type = SMB2_ENCRYPTION_AES128_CCM; + + security_blob = smb2_get_data_area_len(&blob_offset, &blob_length, +- (struct smb2_sync_hdr *)rsp); ++ (struct smb2_hdr *)rsp); + /* + * See MS-SMB2 section 2.2.4: if no blob, client picks default which + * for us will be +@@ -1272,13 +1271,13 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_ + return rc; + + if (sess_data->ses->binding) { +- req->sync_hdr.SessionId = sess_data->ses->Suid; +- req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; ++ req->hdr.SessionId = cpu_to_le64(sess_data->ses->Suid); ++ req->hdr.Flags |= SMB2_FLAGS_SIGNED; + req->PreviousSessionId = 0; + req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; + } else { + /* First session, not a reauthenticate */ +- req->sync_hdr.SessionId = 0; ++ req->hdr.SessionId = 0; + /* + * if reconnect, we need to send previous sess id + * otherwise it is 0 +@@ -1288,7 +1287,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_ + } + + /* enough to enable echos and oplocks and one max size write */ +- req->sync_hdr.CreditRequest = cpu_to_le16(130); ++ req->hdr.CreditRequest = cpu_to_le16(130); + + /* only one of SMB2 signing flags may be set in SMB2 request */ + if (server->sign) +@@ -1447,7 +1446,7 @@ SMB2_auth_kerberos(struct SMB2_sess_data + rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; + /* keep session id and flags if binding */ + if (!ses->binding) { +- ses->Suid = rsp->sync_hdr.SessionId; ++ ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + +@@ -1523,7 +1522,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(stru + + /* If true, rc here is expected and not an error */ + if (sess_data->buf0_type != CIFS_NO_BUFFER && +- rsp->sync_hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) ++ rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) + rc = 0; + + if (rc) +@@ -1545,7 +1544,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(stru + + /* keep existing ses id and flags if binding */ + if (!ses->binding) { +- ses->Suid = rsp->sync_hdr.SessionId; ++ ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + +@@ -1580,7 +1579,7 @@ SMB2_sess_auth_rawntlmssp_authenticate(s + goto out; + + req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; +- req->sync_hdr.SessionId = ses->Suid; ++ req->hdr.SessionId = cpu_to_le64(ses->Suid); + + rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, + sess_data->nls_cp); +@@ -1606,7 +1605,7 @@ SMB2_sess_auth_rawntlmssp_authenticate(s + + /* keep existing ses id and flags if binding */ + if (!ses->binding) { +- ses->Suid = rsp->sync_hdr.SessionId; ++ ses->Suid = le64_to_cpu(rsp->hdr.SessionId); + ses->session_flags = le16_to_cpu(rsp->SessionFlags); + } + +@@ -1737,12 +1736,12 @@ SMB2_logoff(const unsigned int xid, stru + return rc; + + /* since no tcon, smb2_init can not do this, so do here */ +- req->sync_hdr.SessionId = ses->Suid; ++ req->hdr.SessionId = cpu_to_le64(ses->Suid); + + if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) + flags |= CIFS_TRANSFORM_REQ; + else if (server->sign) +- req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; ++ req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + flags |= CIFS_NO_RSP_BUF; + +@@ -1849,14 +1848,14 @@ SMB2_tcon(const unsigned int xid, struct + !(ses->session_flags & + (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) && + ((ses->user_name != NULL) || (ses->sectype == Kerberos))) +- req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; ++ req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + memset(&rqst, 0, sizeof(struct smb_rqst)); + rqst.rq_iov = iov; + rqst.rq_nvec = 2; + + /* Need 64 for max size write so ask for more in case not there yet */ +- req->sync_hdr.CreditRequest = cpu_to_le16(64); ++ req->hdr.CreditRequest = cpu_to_le16(64); + + rc = cifs_send_recv(xid, ses, server, + &rqst, &resp_buftype, flags, &rsp_iov); +@@ -1892,7 +1891,7 @@ SMB2_tcon(const unsigned int xid, struct + tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); + tcon->tidStatus = CifsGood; + tcon->need_reconnect = false; +- tcon->tid = rsp->sync_hdr.TreeId; ++ tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId); + strlcpy(tcon->treeName, tree, sizeof(tcon->treeName)); + + if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && +@@ -1913,9 +1912,8 @@ tcon_exit: + return rc; + + tcon_error_exit: +- if (rsp && rsp->sync_hdr.Status == STATUS_BAD_NETWORK_NAME) { ++ if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME) + cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree); +- } + goto tcon_exit; + } + +@@ -2649,7 +2647,7 @@ int smb311_posix_mkdir(const unsigned in + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + int name_len; + +- req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; ++ req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; + rc = alloc_path_with_tree_prefix(©_path, ©_size, + &name_len, + tcon->treeName, utf16_path); +@@ -2781,7 +2779,7 @@ SMB2_open_init(struct cifs_tcon *tcon, s + if (tcon->share_flags & SHI1005_FLAGS_DFS) { + int name_len; + +- req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; ++ req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; + rc = alloc_path_with_tree_prefix(©_path, ©_size, + &name_len, + tcon->treeName, path); +@@ -2993,7 +2991,7 @@ SMB2_open(const unsigned int xid, struct + oparms->fid->volatile_fid = rsp->VolatileFileId; + oparms->fid->access = oparms->desired_access; + #ifdef CONFIG_CIFS_DEBUG2 +- oparms->fid->mid = le64_to_cpu(rsp->sync_hdr.MessageId); ++ oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId); + #endif /* CIFS_DEBUG2 */ + + if (buf) { +@@ -3102,7 +3100,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, + * response size smaller. + */ + req->MaxOutputResponse = cpu_to_le32(max_response_size); +- req->sync_hdr.CreditCharge = ++ req->hdr.CreditCharge = + cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size), + SMB2_MAX_BUFFER_SIZE)); + /* always an FSCTL (for now) */ +@@ -3110,7 +3108,7 @@ SMB2_ioctl_init(struct cifs_tcon *tcon, + + /* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */ + if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) +- req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; ++ req->hdr.Flags |= SMB2_FLAGS_SIGNED; + + return 0; + } +@@ -3740,7 +3738,7 @@ smb2_echo_callback(struct mid_q_entry *m + + if (mid->mid_state == MID_RESPONSE_RECEIVED + || mid->mid_state == MID_RESPONSE_MALFORMED) { +- credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest); ++ credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + } + +@@ -3840,7 +3838,7 @@ SMB2_echo(struct TCP_Server_Info *server + if (rc) + return rc; + +- req->sync_hdr.CreditRequest = cpu_to_le16(1); ++ req->hdr.CreditRequest = cpu_to_le16(1); + + iov[0].iov_len = total_len; + iov[0].iov_base = (char *)req; +@@ -3944,7 +3942,7 @@ smb2_new_read_req(void **buf, unsigned i + { + int rc = -EACCES; + struct smb2_read_plain_req *req = NULL; +- struct smb2_sync_hdr *shdr; ++ struct smb2_hdr *shdr; + struct TCP_Server_Info *server = io_parms->server; + + rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server, +@@ -3955,8 +3953,8 @@ smb2_new_read_req(void **buf, unsigned i + if (server == NULL) + return -ECONNABORTED; + +- shdr = &req->sync_hdr; +- shdr->ProcessId = cpu_to_le32(io_parms->pid); ++ shdr = &req->hdr; ++ shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; +@@ -4017,8 +4015,8 @@ smb2_new_read_req(void **buf, unsigned i + * Related requests use info from previous read request + * in chain. + */ +- shdr->SessionId = 0xFFFFFFFFFFFFFFFF; +- shdr->TreeId = 0xFFFFFFFF; ++ shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF); ++ shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF); + req->PersistentFileId = 0xFFFFFFFFFFFFFFFF; + req->VolatileFileId = 0xFFFFFFFFFFFFFFFF; + } +@@ -4038,8 +4036,8 @@ smb2_readv_callback(struct mid_q_entry * + struct cifs_readdata *rdata = mid->callback_data; + struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink); + struct TCP_Server_Info *server = rdata->server; +- struct smb2_sync_hdr *shdr = +- (struct smb2_sync_hdr *)rdata->iov[0].iov_base; ++ struct smb2_hdr *shdr = ++ (struct smb2_hdr *)rdata->iov[0].iov_base; + struct cifs_credits credits = { .value = 0, .instance = 0 }; + struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], + .rq_nvec = 1, }; +@@ -4128,7 +4126,7 @@ smb2_async_readv(struct cifs_readdata *r + { + int rc, flags = 0; + char *buf; +- struct smb2_sync_hdr *shdr; ++ struct smb2_hdr *shdr; + struct cifs_io_parms io_parms; + struct smb_rqst rqst = { .rq_iov = rdata->iov, + .rq_nvec = 1 }; +@@ -4161,7 +4159,7 @@ smb2_async_readv(struct cifs_readdata *r + rdata->iov[0].iov_base = buf; + rdata->iov[0].iov_len = total_len; + +- shdr = (struct smb2_sync_hdr *)buf; ++ shdr = (struct smb2_hdr *)buf; + + if (rdata->credits.value > 0) { + shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes, +@@ -4294,7 +4292,7 @@ smb2_writev_callback(struct mid_q_entry + + switch (mid->mid_state) { + case MID_RESPONSE_RECEIVED: +- credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest); ++ credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + wdata->result = smb2_check_receive(mid, server, 0); + if (wdata->result != 0) +@@ -4320,7 +4318,7 @@ smb2_writev_callback(struct mid_q_entry + wdata->result = -EAGAIN; + break; + case MID_RESPONSE_MALFORMED: +- credits.value = le16_to_cpu(rsp->sync_hdr.CreditRequest); ++ credits.value = le16_to_cpu(rsp->hdr.CreditRequest); + credits.instance = server->reconnect_instance; + fallthrough; + default: +@@ -4367,7 +4365,7 @@ smb2_async_writev(struct cifs_writedata + { + int rc = -EACCES, flags = 0; + struct smb2_write_req *req = NULL; +- struct smb2_sync_hdr *shdr; ++ struct smb2_hdr *shdr; + struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink); + struct TCP_Server_Info *server = wdata->server; + struct kvec iov[1]; +@@ -4385,8 +4383,8 @@ smb2_async_writev(struct cifs_writedata + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + +- shdr = (struct smb2_sync_hdr *)req; +- shdr->ProcessId = cpu_to_le32(wdata->cfile->pid); ++ shdr = (struct smb2_hdr *)req; ++ shdr->Id.SyncId.ProcessId = cpu_to_le32(wdata->cfile->pid); + + req->PersistentFileId = wdata->cfile->fid.persistent_fid; + req->VolatileFileId = wdata->cfile->fid.volatile_fid; +@@ -4537,7 +4535,7 @@ SMB2_write(const unsigned int xid, struc + if (smb3_encryption_required(io_parms->tcon)) + flags |= CIFS_TRANSFORM_REQ; + +- req->sync_hdr.ProcessId = cpu_to_le32(io_parms->pid); ++ req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid); + + req->PersistentFileId = io_parms->persistent_fid; + req->VolatileFileId = io_parms->volatile_fid; +@@ -4922,7 +4920,7 @@ SMB2_query_directory(const unsigned int + + if (rc) { + if (rc == -ENODATA && +- rsp->sync_hdr.Status == STATUS_NO_MORE_FILES) { ++ rsp->hdr.Status == STATUS_NO_MORE_FILES) { + trace_smb3_query_dir_done(xid, persistent_fid, + tcon->tid, tcon->ses->Suid, index, 0); + srch_inf->endOfSearch = true; +@@ -4970,7 +4968,7 @@ SMB2_set_info_init(struct cifs_tcon *tco + if (rc) + return rc; + +- req->sync_hdr.ProcessId = cpu_to_le32(pid); ++ req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); + req->InfoType = info_type; + req->FileInfoClass = info_class; + req->PersistentFileId = persistent_fid; +@@ -5130,7 +5128,7 @@ SMB2_oplock_break(const unsigned int xid + req->VolatileFid = volatile_fid; + req->PersistentFid = persistent_fid; + req->OplockLevel = oplock_level; +- req->sync_hdr.CreditRequest = cpu_to_le16(1); ++ req->hdr.CreditRequest = cpu_to_le16(1); + + flags |= CIFS_NO_RSP_BUF; + +@@ -5437,7 +5435,7 @@ smb2_lockv(const unsigned int xid, struc + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + +- req->sync_hdr.ProcessId = cpu_to_le32(pid); ++ req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid); + req->LockCount = cpu_to_le16(num_lock); + + req->PersistentFileId = persist_fid; +@@ -5513,7 +5511,7 @@ SMB2_lease_break(const unsigned int xid, + if (smb3_encryption_required(tcon)) + flags |= CIFS_TRANSFORM_REQ; + +- req->sync_hdr.CreditRequest = cpu_to_le16(1); ++ req->hdr.CreditRequest = cpu_to_le16(1); + req->StructureSize = cpu_to_le16(36); + total_len += 12; + +--- a/fs/cifs/smb2pdu.h ++++ b/fs/cifs/smb2pdu.h +@@ -14,156 +14,12 @@ + #include + #include "cifsacl.h" + +-/* +- * Note that, due to trying to use names similar to the protocol specifications, +- * there are many mixed case field names in the structures below. Although +- * this does not match typical Linux kernel style, it is necessary to be +- * able to match against the protocol specfication. +- * +- * SMB2 commands +- * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses +- * (ie no useful data other than the SMB error code itself) and are marked such. +- * Knowing this helps avoid response buffer allocations and copy in some cases. +- */ +- +-/* List of commands in host endian */ +-#define SMB2_NEGOTIATE_HE 0x0000 +-#define SMB2_SESSION_SETUP_HE 0x0001 +-#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ +-#define SMB2_TREE_CONNECT_HE 0x0003 +-#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ +-#define SMB2_CREATE_HE 0x0005 +-#define SMB2_CLOSE_HE 0x0006 +-#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ +-#define SMB2_READ_HE 0x0008 +-#define SMB2_WRITE_HE 0x0009 +-#define SMB2_LOCK_HE 0x000A +-#define SMB2_IOCTL_HE 0x000B +-#define SMB2_CANCEL_HE 0x000C +-#define SMB2_ECHO_HE 0x000D +-#define SMB2_QUERY_DIRECTORY_HE 0x000E +-#define SMB2_CHANGE_NOTIFY_HE 0x000F +-#define SMB2_QUERY_INFO_HE 0x0010 +-#define SMB2_SET_INFO_HE 0x0011 +-#define SMB2_OPLOCK_BREAK_HE 0x0012 +- +-/* The same list in little endian */ +-#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) +-#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) +-#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) +-#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) +-#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) +-#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) +-#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) +-#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) +-#define SMB2_READ cpu_to_le16(SMB2_READ_HE) +-#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) +-#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) +-#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) +-#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) +-#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) +-#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) +-#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) +-#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) +-#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) +-#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) +- +-#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF) +- +-#define NUMBER_OF_SMB2_COMMANDS 0x0013 +- + /* 52 transform hdr + 64 hdr + 88 create rsp */ + #define SMB2_TRANSFORM_HEADER_SIZE 52 + #define MAX_SMB2_HDR_SIZE 204 + +-#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) +-#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) +-#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc) +- +-/* +- * SMB2 Header Definition +- * +- * "MBZ" : Must be Zero +- * "BB" : BugBug, Something to check/review/analyze later +- * "PDU" : "Protocol Data Unit" (ie a network "frame") +- * +- */ +- +-#define SMB2_HEADER_STRUCTURE_SIZE cpu_to_le16(64) +- +-struct smb2_sync_hdr { +- __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ +- __le16 StructureSize; /* 64 */ +- __le16 CreditCharge; /* MBZ */ +- __le32 Status; /* Error from server */ +- __le16 Command; +- __le16 CreditRequest; /* CreditResponse */ +- __le32 Flags; +- __le32 NextCommand; +- __le64 MessageId; +- __le32 ProcessId; +- __u32 TreeId; /* opaque - so do not make little endian */ +- __u64 SessionId; /* opaque - so do not make little endian */ +- __u8 Signature[16]; +-} __packed; +- + /* The total header size for SMB2 read and write */ +-#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_sync_hdr)) +- +-struct smb2_sync_pdu { +- struct smb2_sync_hdr sync_hdr; +- __le16 StructureSize2; /* size of wct area (varies, request specific) */ +-} __packed; +- +-#define SMB3_AES_CCM_NONCE 11 +-#define SMB3_AES_GCM_NONCE 12 +- +-/* Transform flags (for 3.0 dialect this flag indicates CCM */ +-#define TRANSFORM_FLAG_ENCRYPTED 0x0001 +-struct smb2_transform_hdr { +- __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ +- __u8 Signature[16]; +- __u8 Nonce[16]; +- __le32 OriginalMessageSize; +- __u16 Reserved1; +- __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */ +- __u64 SessionId; +-} __packed; +- +-/* See MS-SMB2 2.2.42 */ +-struct smb2_compression_transform_hdr_unchained { +- __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ +- __le32 OriginalCompressedSegmentSize; +- __le16 CompressionAlgorithm; +- __le16 Flags; +- __le16 Length; /* if chained it is length, else offset */ +-} __packed; +- +-/* See MS-SMB2 2.2.42.1 */ +-#define SMB2_COMPRESSION_FLAG_NONE 0x0000 +-#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001 +- +-struct compression_payload_header { +- __le16 CompressionAlgorithm; +- __le16 Flags; +- __le32 Length; /* length of compressed playload including field below if present */ +- /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */ +-} __packed; +- +-/* See MS-SMB2 2.2.42.2 */ +-struct smb2_compression_transform_hdr_chained { +- __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ +- __le32 OriginalCompressedSegmentSize; +- /* struct compression_payload_header[] */ +-} __packed; +- +-/* See MS-SMB2 2.2.42.2.2 */ +-struct compression_pattern_payload_v1 { +- __le16 Pattern; +- __le16 Reserved1; +- __le16 Reserved2; +- __le32 Repetitions; +-} __packed; ++#define SMB2_READWRITE_PDU_HEADER_SIZE (48 + sizeof(struct smb2_hdr)) + + /* See MS-SMB2 2.2.43 */ + struct smb2_rdma_transform { +@@ -190,17 +46,6 @@ struct smb2_rdma_crypto_transform { + } __packed; + + /* +- * SMB2 flag definitions +- */ +-#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) +-#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) +-#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) +-#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) +-#define SMB2_FLAGS_PRIORITY_MASK cpu_to_le32(0x00000070) /* SMB3.1.1 */ +-#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) +-#define SMB2_FLAGS_REPLAY_OPERATION cpu_to_le32(0x20000000) /* SMB3 & up */ +- +-/* + * Definitions for SMB2 Protocol Data Units (network frames) + * + * See MS-SMB2.PDF specification for protocol details. +@@ -214,7 +59,7 @@ struct smb2_rdma_crypto_transform { + #define SMB2_ERROR_STRUCTURE_SIZE2 cpu_to_le16(9) + + struct smb2_err_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Reserved; /* MBZ */ + __le32 ByteCount; /* even if zero, at least one byte follows */ +@@ -273,7 +118,7 @@ struct share_redirect_error_context_rsp + #define SMB2_CLIENT_GUID_SIZE 16 + + struct smb2_negotiate_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 DialectCount; + __le16 SecurityMode; +@@ -472,7 +317,7 @@ struct smb2_posix_neg_context { + } __packed; + + struct smb2_negotiate_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 65 */ + __le16 SecurityMode; + __le16 DialectRevision; +@@ -495,7 +340,7 @@ struct smb2_negotiate_rsp { + #define SMB2_SESSION_REQ_FLAG_ENCRYPT_DATA 0x04 + + struct smb2_sess_setup_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 25 */ + __u8 Flags; + __u8 SecurityMode; +@@ -512,7 +357,7 @@ struct smb2_sess_setup_req { + #define SMB2_SESSION_FLAG_IS_NULL 0x0002 + #define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004 + struct smb2_sess_setup_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 SessionFlags; + __le16 SecurityBufferOffset; +@@ -521,161 +366,13 @@ struct smb2_sess_setup_rsp { + } __packed; + + struct smb2_logoff_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; + } __packed; + + struct smb2_logoff_rsp { +- struct smb2_sync_hdr sync_hdr; +- __le16 StructureSize; /* Must be 4 */ +- __le16 Reserved; +-} __packed; +- +-/* Flags/Reserved for SMB3.1.1 */ +-#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001) +-#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002) +-#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004) +- +-struct smb2_tree_connect_req { +- struct smb2_sync_hdr sync_hdr; +- __le16 StructureSize; /* Must be 9 */ +- __le16 Flags; /* Reserved MBZ for dialects prior to SMB3.1.1 */ +- __le16 PathOffset; +- __le16 PathLength; +- __u8 Buffer[]; /* variable length */ +-} __packed; +- +-/* See MS-SMB2 section 2.2.9.2 */ +-/* Context Types */ +-#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000 +-#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001) +- +-struct tree_connect_contexts { +- __le16 ContextType; +- __le16 DataLength; +- __le32 Reserved; +- __u8 Data[]; +-} __packed; +- +-/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */ +-struct smb3_blob_data { +- __le16 BlobSize; +- __u8 BlobData[]; +-} __packed; +- +-/* Valid values for Attr */ +-#define SE_GROUP_MANDATORY 0x00000001 +-#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 +-#define SE_GROUP_ENABLED 0x00000004 +-#define SE_GROUP_OWNER 0x00000008 +-#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010 +-#define SE_GROUP_INTEGRITY 0x00000020 +-#define SE_GROUP_INTEGRITY_ENABLED 0x00000040 +-#define SE_GROUP_RESOURCE 0x20000000 +-#define SE_GROUP_LOGON_ID 0xC0000000 +- +-/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */ +- +-struct sid_array_data { +- __le16 SidAttrCount; +- /* SidAttrList - array of sid_attr_data structs */ +-} __packed; +- +-struct luid_attr_data { +- +-} __packed; +- +-/* +- * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5 +- * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA +- */ +- +-struct privilege_array_data { +- __le16 PrivilegeCount; +- /* array of privilege_data structs */ +-} __packed; +- +-struct remoted_identity_tcon_context { +- __le16 TicketType; /* must be 0x0001 */ +- __le16 TicketSize; /* total size of this struct */ +- __le16 User; /* offset to SID_ATTR_DATA struct with user info */ +- __le16 UserName; /* offset to null terminated Unicode username string */ +- __le16 Domain; /* offset to null terminated Unicode domain name */ +- __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */ +- __le16 RestrictedGroups; /* similar to above */ +- __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */ +- __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */ +- __le16 Owner; /* offset to BLOB_DATA struct */ +- __le16 DefaultDacl; /* offset to BLOB_DATA struct */ +- __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */ +- __le16 UserClaims; /* offset to BLOB_DATA struct */ +- __le16 DeviceClaims; /* offset to BLOB_DATA struct */ +- __u8 TicketInfo[]; /* variable length buf - remoted identity data */ +-} __packed; +- +-struct smb2_tree_connect_req_extension { +- __le32 TreeConnectContextOffset; +- __le16 TreeConnectContextCount; +- __u8 Reserved[10]; +- __u8 PathName[]; /* variable sized array */ +- /* followed by array of TreeConnectContexts */ +-} __packed; +- +-struct smb2_tree_connect_rsp { +- struct smb2_sync_hdr sync_hdr; +- __le16 StructureSize; /* Must be 16 */ +- __u8 ShareType; /* see below */ +- __u8 Reserved; +- __le32 ShareFlags; /* see below */ +- __le32 Capabilities; /* see below */ +- __le32 MaximalAccess; +-} __packed; +- +-/* Possible ShareType values */ +-#define SMB2_SHARE_TYPE_DISK 0x01 +-#define SMB2_SHARE_TYPE_PIPE 0x02 +-#define SMB2_SHARE_TYPE_PRINT 0x03 +- +-/* +- * Possible ShareFlags - exactly one and only one of the first 4 caching flags +- * must be set (any of the remaining, SHI1005, flags may be set individually +- * or in combination. +- */ +-#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 +-#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 +-#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 +-#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 +-#define SHI1005_FLAGS_DFS 0x00000001 +-#define SHI1005_FLAGS_DFS_ROOT 0x00000002 +-#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 +-#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 +-#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 +-#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 +-#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 +-#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000 +-#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000 +-#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000 +-#define SMB2_SHAREFLAG_IDENTITY_REMOTING 0x00040000 /* 3.1.1 */ +-#define SMB2_SHAREFLAG_COMPRESS_DATA 0x00100000 /* 3.1.1 */ +-#define SHI1005_FLAGS_ALL 0x0014FF33 +- +-/* Possible share capabilities */ +-#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ +-#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ +-#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */ +-#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */ +-#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ +-#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */ +- +-struct smb2_tree_disconnect_req { +- struct smb2_sync_hdr sync_hdr; +- __le16 StructureSize; /* Must be 4 */ +- __le16 Reserved; +-} __packed; +- +-struct smb2_tree_disconnect_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; + } __packed; +@@ -808,7 +505,7 @@ struct smb2_tree_disconnect_rsp { + #define SMB2_CREATE_IOV_SIZE 8 + + struct smb2_create_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u8 SecurityFlags; + __u8 RequestedOplockLevel; +@@ -835,7 +532,7 @@ struct smb2_create_req { + #define MAX_SMB2_CREATE_RESPONSE_SIZE 880 + + struct smb2_create_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 89 */ + __u8 OplockLevel; + __u8 Flag; /* 0x01 if reparse point */ +@@ -1210,7 +907,7 @@ struct duplicate_extents_to_file { + #define SMB2_IOCTL_IOV_SIZE 2 + + struct smb2_ioctl_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u16 Reserved; + __le32 CtlCode; +@@ -1228,7 +925,7 @@ struct smb2_ioctl_req { + } __packed; + + struct smb2_ioctl_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 57 */ + __u16 Reserved; + __le32 CtlCode; +@@ -1246,7 +943,7 @@ struct smb2_ioctl_rsp { + /* Currently defined values for close flags */ + #define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001) + struct smb2_close_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Flags; + __le32 Reserved; +@@ -1260,7 +957,7 @@ struct smb2_close_req { + #define MAX_SMB2_CLOSE_RESPONSE_SIZE 124 + + struct smb2_close_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* 60 */ + __le16 Flags; + __le32 Reserved; +@@ -1274,7 +971,7 @@ struct smb2_close_rsp { + } __packed; + + struct smb2_flush_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __le16 Reserved1; + __le32 Reserved2; +@@ -1283,7 +980,7 @@ struct smb2_flush_req { + } __packed; + + struct smb2_flush_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Reserved; + } __packed; +@@ -1300,7 +997,7 @@ struct smb2_flush_rsp { + + /* SMB2 read request without RFC1001 length at the beginning */ + struct smb2_read_plain_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __u8 Padding; /* offset from start of SMB2 header to place read */ + __u8 Flags; /* MBZ unless SMB3.02 or later */ +@@ -1321,7 +1018,7 @@ struct smb2_read_plain_req { + #define SMB2_READFLAG_RESPONSE_RDMA_TRANSFORM 0x00000001 + + struct smb2_read_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; +@@ -1336,7 +1033,7 @@ struct smb2_read_rsp { + #define SMB2_WRITEFLAG_WRITE_UNBUFFERED 0x00000002 /* SMB3.02 or later */ + + struct smb2_write_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 49 */ + __le16 DataOffset; /* offset from start of SMB2 header to write data */ + __le32 Length; +@@ -1352,7 +1049,7 @@ struct smb2_write_req { + } __packed; + + struct smb2_write_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 17 */ + __u8 DataOffset; + __u8 Reserved; +@@ -1380,7 +1077,7 @@ struct smb2_write_rsp { + #define FILE_NOTIFY_CHANGE_STREAM_WRITE 0x00000800 + + struct smb2_change_notify_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; + __le16 Flags; + __le32 OutputBufferLength; +@@ -1391,7 +1088,7 @@ struct smb2_change_notify_req { + } __packed; + + struct smb2_change_notify_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; +@@ -1411,7 +1108,7 @@ struct smb2_lock_element { + } __packed; + + struct smb2_lock_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 48 */ + __le16 LockCount; + /* +@@ -1429,19 +1126,19 @@ struct smb2_lock_req { + } __packed; + + struct smb2_lock_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __le16 Reserved; + } __packed; + + struct smb2_echo_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; + } __packed; + + struct smb2_echo_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 4 */ + __u16 Reserved; + } __packed; +@@ -1471,7 +1168,7 @@ struct smb2_echo_rsp { + */ + + struct smb2_query_directory_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 FileInformationClass; + __u8 Flags; +@@ -1485,7 +1182,7 @@ struct smb2_query_directory_req { + } __packed; + + struct smb2_query_directory_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; +@@ -1518,7 +1215,7 @@ struct smb2_query_directory_rsp { + #define SL_INDEX_SPECIFIED 0x00000004 + + struct smb2_query_info_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 41 */ + __u8 InfoType; + __u8 FileInfoClass; +@@ -1534,7 +1231,7 @@ struct smb2_query_info_req { + } __packed; + + struct smb2_query_info_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 9 */ + __le16 OutputBufferOffset; + __le32 OutputBufferLength; +@@ -1551,7 +1248,7 @@ struct smb2_query_info_rsp { + #define SMB2_SET_INFO_IOV_SIZE 3 + + struct smb2_set_info_req { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 33 */ + __u8 InfoType; + __u8 FileInfoClass; +@@ -1565,12 +1262,12 @@ struct smb2_set_info_req { + } __packed; + + struct smb2_set_info_rsp { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 2 */ + } __packed; + + struct smb2_oplock_break { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 24 */ + __u8 OplockLevel; + __u8 Reserved; +@@ -1582,7 +1279,7 @@ struct smb2_oplock_break { + #define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01) + + struct smb2_lease_break { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 44 */ + __le16 Epoch; + __le32 Flags; +@@ -1595,7 +1292,7 @@ struct smb2_lease_break { + } __packed; + + struct smb2_lease_ack { +- struct smb2_sync_hdr sync_hdr; ++ struct smb2_hdr hdr; + __le16 StructureSize; /* Must be 36 */ + __le16 Reserved; + __le32 Flags; +--- a/fs/cifs/smb2proto.h ++++ b/fs/cifs/smb2proto.h +@@ -25,7 +25,7 @@ extern int smb2_check_message(char *buf, + struct TCP_Server_Info *server); + extern unsigned int smb2_calc_size(void *buf, struct TCP_Server_Info *server); + extern char *smb2_get_data_area_len(int *off, int *len, +- struct smb2_sync_hdr *shdr); ++ struct smb2_hdr *shdr); + extern __le16 *cifs_convert_path_to_utf16(const char *from, + struct cifs_sb_info *cifs_sb); + +--- a/fs/cifs/smb2transport.c ++++ b/fs/cifs/smb2transport.c +@@ -19,7 +19,6 @@ + #include + #include + #include +-#include "smb2pdu.h" + #include "cifsglob.h" + #include "cifsproto.h" + #include "smb2proto.h" +@@ -213,14 +212,14 @@ smb2_calc_signature(struct smb_rqst *rqs + unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; + unsigned char *sigptr = smb2_signature; + struct kvec *iov = rqst->rq_iov; +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; + struct cifs_ses *ses; + struct shash_desc *shash; + struct crypto_shash *hash; + struct sdesc *sdesc = NULL; + struct smb_rqst drqst; + +- ses = smb2_find_smb_ses(server, shdr->SessionId); ++ ses = smb2_find_smb_ses(server, le64_to_cpu(shdr->SessionId)); + if (unlikely(!ses)) { + cifs_server_dbg(VFS, "%s: Could not find session\n", __func__); + return -ENOENT; +@@ -549,14 +548,14 @@ smb3_calc_signature(struct smb_rqst *rqs + unsigned char smb3_signature[SMB2_CMACAES_SIZE]; + unsigned char *sigptr = smb3_signature; + struct kvec *iov = rqst->rq_iov; +- struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)iov[0].iov_base; ++ struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base; + struct shash_desc *shash; + struct crypto_shash *hash; + struct sdesc *sdesc = NULL; + struct smb_rqst drqst; + u8 key[SMB3_SIGN_KEY_SIZE]; + +- rc = smb2_get_sign_key(shdr->SessionId, server, key); ++ rc = smb2_get_sign_key(le64_to_cpu(shdr->SessionId), server, key); + if (unlikely(rc)) { + cifs_server_dbg(VFS, "%s: Could not get signing key\n", __func__); + return rc; +@@ -628,12 +627,12 @@ static int + smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) + { + int rc = 0; +- struct smb2_sync_hdr *shdr; ++ struct smb2_hdr *shdr; + struct smb2_sess_setup_req *ssr; + bool is_binding; + bool is_signed; + +- shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; ++ shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + ssr = (struct smb2_sess_setup_req *)shdr; + + is_binding = shdr->Command == SMB2_SESSION_SETUP && +@@ -659,8 +658,8 @@ smb2_verify_signature(struct smb_rqst *r + { + unsigned int rc; + char server_response_sig[SMB2_SIGNATURE_SIZE]; +- struct smb2_sync_hdr *shdr = +- (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; ++ struct smb2_hdr *shdr = ++ (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + + if ((shdr->Command == SMB2_NEGOTIATE) || + (shdr->Command == SMB2_SESSION_SETUP) || +@@ -706,7 +705,7 @@ smb2_verify_signature(struct smb_rqst *r + */ + static inline void + smb2_seq_num_into_buf(struct TCP_Server_Info *server, +- struct smb2_sync_hdr *shdr) ++ struct smb2_hdr *shdr) + { + unsigned int i, num = le16_to_cpu(shdr->CreditCharge); + +@@ -717,7 +716,7 @@ smb2_seq_num_into_buf(struct TCP_Server_ + } + + static struct mid_q_entry * +-smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr, ++smb2_mid_entry_alloc(const struct smb2_hdr *shdr, + struct TCP_Server_Info *server) + { + struct mid_q_entry *temp; +@@ -749,14 +748,15 @@ smb2_mid_entry_alloc(const struct smb2_s + + atomic_inc(&midCount); + temp->mid_state = MID_REQUEST_ALLOCATED; +- trace_smb3_cmd_enter(shdr->TreeId, shdr->SessionId, +- le16_to_cpu(shdr->Command), temp->mid); ++ trace_smb3_cmd_enter(le32_to_cpu(shdr->Id.SyncId.TreeId), ++ le64_to_cpu(shdr->SessionId), ++ le16_to_cpu(shdr->Command), temp->mid); + return temp; + } + + static int + smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, +- struct smb2_sync_hdr *shdr, struct mid_q_entry **mid) ++ struct smb2_hdr *shdr, struct mid_q_entry **mid) + { + if (server->tcpStatus == CifsExiting) + return -ENOENT; +@@ -824,8 +824,8 @@ smb2_setup_request(struct cifs_ses *ses, + struct smb_rqst *rqst) + { + int rc; +- struct smb2_sync_hdr *shdr = +- (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; ++ struct smb2_hdr *shdr = ++ (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + smb2_seq_num_into_buf(server, shdr); +@@ -850,8 +850,8 @@ struct mid_q_entry * + smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) + { + int rc; +- struct smb2_sync_hdr *shdr = +- (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base; ++ struct smb2_hdr *shdr = ++ (struct smb2_hdr *)rqst->rq_iov[0].iov_base; + struct mid_q_entry *mid; + + if (server->tcpStatus == CifsNeedNegotiate && +--- /dev/null ++++ b/fs/smbfs_common/smb2pdu.h +@@ -0,0 +1,318 @@ ++/* SPDX-License-Identifier: LGPL-2.1 */ ++#ifndef _COMMON_SMB2PDU_H ++#define _COMMON_SMB2PDU_H ++ ++/* ++ * Note that, due to trying to use names similar to the protocol specifications, ++ * there are many mixed case field names in the structures below. Although ++ * this does not match typical Linux kernel style, it is necessary to be ++ * able to match against the protocol specfication. ++ * ++ * SMB2 commands ++ * Some commands have minimal (wct=0,bcc=0), or uninteresting, responses ++ * (ie no useful data other than the SMB error code itself) and are marked such. ++ * Knowing this helps avoid response buffer allocations and copy in some cases. ++ */ ++ ++/* List of commands in host endian */ ++#define SMB2_NEGOTIATE_HE 0x0000 ++#define SMB2_SESSION_SETUP_HE 0x0001 ++#define SMB2_LOGOFF_HE 0x0002 /* trivial request/resp */ ++#define SMB2_TREE_CONNECT_HE 0x0003 ++#define SMB2_TREE_DISCONNECT_HE 0x0004 /* trivial req/resp */ ++#define SMB2_CREATE_HE 0x0005 ++#define SMB2_CLOSE_HE 0x0006 ++#define SMB2_FLUSH_HE 0x0007 /* trivial resp */ ++#define SMB2_READ_HE 0x0008 ++#define SMB2_WRITE_HE 0x0009 ++#define SMB2_LOCK_HE 0x000A ++#define SMB2_IOCTL_HE 0x000B ++#define SMB2_CANCEL_HE 0x000C ++#define SMB2_ECHO_HE 0x000D ++#define SMB2_QUERY_DIRECTORY_HE 0x000E ++#define SMB2_CHANGE_NOTIFY_HE 0x000F ++#define SMB2_QUERY_INFO_HE 0x0010 ++#define SMB2_SET_INFO_HE 0x0011 ++#define SMB2_OPLOCK_BREAK_HE 0x0012 ++ ++/* The same list in little endian */ ++#define SMB2_NEGOTIATE cpu_to_le16(SMB2_NEGOTIATE_HE) ++#define SMB2_SESSION_SETUP cpu_to_le16(SMB2_SESSION_SETUP_HE) ++#define SMB2_LOGOFF cpu_to_le16(SMB2_LOGOFF_HE) ++#define SMB2_TREE_CONNECT cpu_to_le16(SMB2_TREE_CONNECT_HE) ++#define SMB2_TREE_DISCONNECT cpu_to_le16(SMB2_TREE_DISCONNECT_HE) ++#define SMB2_CREATE cpu_to_le16(SMB2_CREATE_HE) ++#define SMB2_CLOSE cpu_to_le16(SMB2_CLOSE_HE) ++#define SMB2_FLUSH cpu_to_le16(SMB2_FLUSH_HE) ++#define SMB2_READ cpu_to_le16(SMB2_READ_HE) ++#define SMB2_WRITE cpu_to_le16(SMB2_WRITE_HE) ++#define SMB2_LOCK cpu_to_le16(SMB2_LOCK_HE) ++#define SMB2_IOCTL cpu_to_le16(SMB2_IOCTL_HE) ++#define SMB2_CANCEL cpu_to_le16(SMB2_CANCEL_HE) ++#define SMB2_ECHO cpu_to_le16(SMB2_ECHO_HE) ++#define SMB2_QUERY_DIRECTORY cpu_to_le16(SMB2_QUERY_DIRECTORY_HE) ++#define SMB2_CHANGE_NOTIFY cpu_to_le16(SMB2_CHANGE_NOTIFY_HE) ++#define SMB2_QUERY_INFO cpu_to_le16(SMB2_QUERY_INFO_HE) ++#define SMB2_SET_INFO cpu_to_le16(SMB2_SET_INFO_HE) ++#define SMB2_OPLOCK_BREAK cpu_to_le16(SMB2_OPLOCK_BREAK_HE) ++ ++#define SMB2_INTERNAL_CMD cpu_to_le16(0xFFFF) ++ ++#define NUMBER_OF_SMB2_COMMANDS 0x0013 ++ ++/* ++ * SMB2 Header Definition ++ * ++ * "MBZ" : Must be Zero ++ * "BB" : BugBug, Something to check/review/analyze later ++ * "PDU" : "Protocol Data Unit" (ie a network "frame") ++ * ++ */ ++ ++#define __SMB2_HEADER_STRUCTURE_SIZE 64 ++#define SMB2_HEADER_STRUCTURE_SIZE \ ++ cpu_to_le16(__SMB2_HEADER_STRUCTURE_SIZE) ++ ++#define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe) ++#define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd) ++#define SMB2_COMPRESSION_TRANSFORM_ID cpu_to_le32(0x424d53fc) ++ ++/* ++ * SMB2 flag definitions ++ */ ++#define SMB2_FLAGS_SERVER_TO_REDIR cpu_to_le32(0x00000001) ++#define SMB2_FLAGS_ASYNC_COMMAND cpu_to_le32(0x00000002) ++#define SMB2_FLAGS_RELATED_OPERATIONS cpu_to_le32(0x00000004) ++#define SMB2_FLAGS_SIGNED cpu_to_le32(0x00000008) ++#define SMB2_FLAGS_PRIORITY_MASK cpu_to_le32(0x00000070) /* SMB3.1.1 */ ++#define SMB2_FLAGS_DFS_OPERATIONS cpu_to_le32(0x10000000) ++#define SMB2_FLAGS_REPLAY_OPERATION cpu_to_le32(0x20000000) /* SMB3 & up */ ++ ++/* See MS-SMB2 section 2.2.1 */ ++struct smb2_hdr { ++ __le32 ProtocolId; /* 0xFE 'S' 'M' 'B' */ ++ __le16 StructureSize; /* 64 */ ++ __le16 CreditCharge; /* MBZ */ ++ __le32 Status; /* Error from server */ ++ __le16 Command; ++ __le16 CreditRequest; /* CreditResponse */ ++ __le32 Flags; ++ __le32 NextCommand; ++ __le64 MessageId; ++ union { ++ struct { ++ __le32 ProcessId; ++ __le32 TreeId; ++ } __packed SyncId; ++ __le64 AsyncId; ++ } __packed Id; ++ __le64 SessionId; ++ __u8 Signature[16]; ++} __packed; ++ ++struct smb2_pdu { ++ struct smb2_hdr hdr; ++ __le16 StructureSize2; /* size of wct area (varies, request specific) */ ++} __packed; ++ ++#define SMB3_AES_CCM_NONCE 11 ++#define SMB3_AES_GCM_NONCE 12 ++ ++/* Transform flags (for 3.0 dialect this flag indicates CCM */ ++#define TRANSFORM_FLAG_ENCRYPTED 0x0001 ++struct smb2_transform_hdr { ++ __le32 ProtocolId; /* 0xFD 'S' 'M' 'B' */ ++ __u8 Signature[16]; ++ __u8 Nonce[16]; ++ __le32 OriginalMessageSize; ++ __u16 Reserved1; ++ __le16 Flags; /* EncryptionAlgorithm for 3.0, enc enabled for 3.1.1 */ ++ __le64 SessionId; ++} __packed; ++ ++ ++/* See MS-SMB2 2.2.42 */ ++struct smb2_compression_transform_hdr_unchained { ++ __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ ++ __le32 OriginalCompressedSegmentSize; ++ __le16 CompressionAlgorithm; ++ __le16 Flags; ++ __le16 Length; /* if chained it is length, else offset */ ++} __packed; ++ ++/* See MS-SMB2 2.2.42.1 */ ++#define SMB2_COMPRESSION_FLAG_NONE 0x0000 ++#define SMB2_COMPRESSION_FLAG_CHAINED 0x0001 ++ ++struct compression_payload_header { ++ __le16 CompressionAlgorithm; ++ __le16 Flags; ++ __le32 Length; /* length of compressed playload including field below if present */ ++ /* __le32 OriginalPayloadSize; */ /* optional, present when LZNT1, LZ77, LZ77+Huffman */ ++} __packed; ++ ++/* See MS-SMB2 2.2.42.2 */ ++struct smb2_compression_transform_hdr_chained { ++ __le32 ProtocolId; /* 0xFC 'S' 'M' 'B' */ ++ __le32 OriginalCompressedSegmentSize; ++ /* struct compression_payload_header[] */ ++} __packed; ++ ++/* See MS-SMB2 2.2.42.2.2 */ ++struct compression_pattern_payload_v1 { ++ __le16 Pattern; ++ __le16 Reserved1; ++ __le16 Reserved2; ++ __le32 Repetitions; ++} __packed; ++ ++/* See MS-SMB2 section 2.2.9.2 */ ++/* Context Types */ ++#define SMB2_RESERVED_TREE_CONNECT_CONTEXT_ID 0x0000 ++#define SMB2_REMOTED_IDENTITY_TREE_CONNECT_CONTEXT_ID cpu_to_le16(0x0001) ++ ++struct tree_connect_contexts { ++ __le16 ContextType; ++ __le16 DataLength; ++ __le32 Reserved; ++ __u8 Data[]; ++} __packed; ++ ++/* Remoted identity tree connect context structures - see MS-SMB2 2.2.9.2.1 */ ++struct smb3_blob_data { ++ __le16 BlobSize; ++ __u8 BlobData[]; ++} __packed; ++ ++/* Valid values for Attr */ ++#define SE_GROUP_MANDATORY 0x00000001 ++#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 ++#define SE_GROUP_ENABLED 0x00000004 ++#define SE_GROUP_OWNER 0x00000008 ++#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010 ++#define SE_GROUP_INTEGRITY 0x00000020 ++#define SE_GROUP_INTEGRITY_ENABLED 0x00000040 ++#define SE_GROUP_RESOURCE 0x20000000 ++#define SE_GROUP_LOGON_ID 0xC0000000 ++ ++/* struct sid_attr_data is SidData array in BlobData format then le32 Attr */ ++ ++struct sid_array_data { ++ __le16 SidAttrCount; ++ /* SidAttrList - array of sid_attr_data structs */ ++} __packed; ++ ++struct luid_attr_data { ++ ++} __packed; ++ ++/* ++ * struct privilege_data is the same as BLOB_DATA - see MS-SMB2 2.2.9.2.1.5 ++ * but with size of LUID_ATTR_DATA struct and BlobData set to LUID_ATTR DATA ++ */ ++ ++struct privilege_array_data { ++ __le16 PrivilegeCount; ++ /* array of privilege_data structs */ ++} __packed; ++ ++struct remoted_identity_tcon_context { ++ __le16 TicketType; /* must be 0x0001 */ ++ __le16 TicketSize; /* total size of this struct */ ++ __le16 User; /* offset to SID_ATTR_DATA struct with user info */ ++ __le16 UserName; /* offset to null terminated Unicode username string */ ++ __le16 Domain; /* offset to null terminated Unicode domain name */ ++ __le16 Groups; /* offset to SID_ARRAY_DATA struct with group info */ ++ __le16 RestrictedGroups; /* similar to above */ ++ __le16 Privileges; /* offset to PRIVILEGE_ARRAY_DATA struct */ ++ __le16 PrimaryGroup; /* offset to SID_ARRAY_DATA struct */ ++ __le16 Owner; /* offset to BLOB_DATA struct */ ++ __le16 DefaultDacl; /* offset to BLOB_DATA struct */ ++ __le16 DeviceGroups; /* offset to SID_ARRAY_DATA struct */ ++ __le16 UserClaims; /* offset to BLOB_DATA struct */ ++ __le16 DeviceClaims; /* offset to BLOB_DATA struct */ ++ __u8 TicketInfo[]; /* variable length buf - remoted identity data */ ++} __packed; ++ ++struct smb2_tree_connect_req_extension { ++ __le32 TreeConnectContextOffset; ++ __le16 TreeConnectContextCount; ++ __u8 Reserved[10]; ++ __u8 PathName[]; /* variable sized array */ ++ /* followed by array of TreeConnectContexts */ ++} __packed; ++ ++/* Flags/Reserved for SMB3.1.1 */ ++#define SMB2_TREE_CONNECT_FLAG_CLUSTER_RECONNECT cpu_to_le16(0x0001) ++#define SMB2_TREE_CONNECT_FLAG_REDIRECT_TO_OWNER cpu_to_le16(0x0002) ++#define SMB2_TREE_CONNECT_FLAG_EXTENSION_PRESENT cpu_to_le16(0x0004) ++ ++struct smb2_tree_connect_req { ++ struct smb2_hdr hdr; ++ __le16 StructureSize; /* Must be 9 */ ++ __le16 Flags; /* Flags in SMB3.1.1 */ ++ __le16 PathOffset; ++ __le16 PathLength; ++ __u8 Buffer[]; /* variable length */ ++} __packed; ++ ++/* Possible ShareType values */ ++#define SMB2_SHARE_TYPE_DISK 0x01 ++#define SMB2_SHARE_TYPE_PIPE 0x02 ++#define SMB2_SHARE_TYPE_PRINT 0x03 ++ ++/* ++ * Possible ShareFlags - exactly one and only one of the first 4 caching flags ++ * must be set (any of the remaining, SHI1005, flags may be set individually ++ * or in combination. ++ */ ++#define SMB2_SHAREFLAG_MANUAL_CACHING 0x00000000 ++#define SMB2_SHAREFLAG_AUTO_CACHING 0x00000010 ++#define SMB2_SHAREFLAG_VDO_CACHING 0x00000020 ++#define SMB2_SHAREFLAG_NO_CACHING 0x00000030 ++#define SHI1005_FLAGS_DFS 0x00000001 ++#define SHI1005_FLAGS_DFS_ROOT 0x00000002 ++#define SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS 0x00000100 ++#define SHI1005_FLAGS_FORCE_SHARED_DELETE 0x00000200 ++#define SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING 0x00000400 ++#define SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM 0x00000800 ++#define SHI1005_FLAGS_FORCE_LEVELII_OPLOCK 0x00001000 ++#define SHI1005_FLAGS_ENABLE_HASH_V1 0x00002000 ++#define SHI1005_FLAGS_ENABLE_HASH_V2 0x00004000 ++#define SHI1005_FLAGS_ENCRYPT_DATA 0x00008000 ++#define SMB2_SHAREFLAG_IDENTITY_REMOTING 0x00040000 /* 3.1.1 */ ++#define SMB2_SHAREFLAG_COMPRESS_DATA 0x00100000 /* 3.1.1 */ ++#define SHI1005_FLAGS_ALL 0x0014FF33 ++ ++/* Possible share capabilities */ ++#define SMB2_SHARE_CAP_DFS cpu_to_le32(0x00000008) /* all dialects */ ++#define SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY cpu_to_le32(0x00000010) /* 3.0 */ ++#define SMB2_SHARE_CAP_SCALEOUT cpu_to_le32(0x00000020) /* 3.0 */ ++#define SMB2_SHARE_CAP_CLUSTER cpu_to_le32(0x00000040) /* 3.0 */ ++#define SMB2_SHARE_CAP_ASYMMETRIC cpu_to_le32(0x00000080) /* 3.02 */ ++#define SMB2_SHARE_CAP_REDIRECT_TO_OWNER cpu_to_le32(0x00000100) /* 3.1.1 */ ++ ++struct smb2_tree_connect_rsp { ++ struct smb2_hdr hdr; ++ __le16 StructureSize; /* Must be 16 */ ++ __u8 ShareType; /* see below */ ++ __u8 Reserved; ++ __le32 ShareFlags; /* see below */ ++ __le32 Capabilities; /* see below */ ++ __le32 MaximalAccess; ++} __packed; ++ ++struct smb2_tree_disconnect_req { ++ struct smb2_hdr hdr; ++ __le16 StructureSize; /* Must be 4 */ ++ __le16 Reserved; ++} __packed; ++ ++struct smb2_tree_disconnect_rsp { ++ struct smb2_hdr hdr; ++ __le16 StructureSize; /* Must be 4 */ ++ __le16 Reserved; ++} __packed; ++ ++ ++#endif /* _COMMON_SMB2PDU_H */ diff --git a/queue-5.15/cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch b/queue-5.15/cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch new file mode 100644 index 0000000000..239ec250bf --- /dev/null +++ b/queue-5.15/cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch @@ -0,0 +1,60 @@ +From stable+bounces-274533-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:04 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 16:02:12 -0400 +Subject: cifs: remove check of list iterator against head past the loop body +To: stable@vger.kernel.org +Cc: Jakob Koschel , "Paulo Alcantara (SUSE)" , Steve French , Sasha Levin +Message-ID: <20260714200215.3152449-2-sashal@kernel.org> + +From: Jakob Koschel + +[ Upstream commit 00c796eecba4898194ea549679797ee28f89a92f ] + +When list_for_each_entry() completes the iteration over the whole list +without breaking the loop, the iterator value will be a bogus pointer +computed based on the head element. + +While it is safe to use the pointer to determine if it was computed +based on the head element, either with list_entry_is_head() or +&pos->member == head, using the iterator variable after the loop should +be avoided. + +In preparation to limit the scope of a list iterator to the list +traversal loop, use a dedicated pointer to point to the found element [1]. + +Link: https://lore.kernel.org/all/CAHk-=wgRr_D8CB-D9Kg-c=EHreAsk5SqXPwr9Y7k9sA6cWXJ6w@mail.gmail.com/ [1] +Reviewed-by: Paulo Alcantara (SUSE) +Signed-off-by: Jakob Koschel +Signed-off-by: Steve French +Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/smb2misc.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/fs/cifs/smb2misc.c ++++ b/fs/cifs/smb2misc.c +@@ -150,16 +150,18 @@ smb2_check_message(char *buf, unsigned i + struct smb2_transform_hdr *thdr = + (struct smb2_transform_hdr *)buf; + struct cifs_ses *ses = NULL; ++ struct cifs_ses *iter; + + /* decrypt frame now that it is completely read in */ + spin_lock(&cifs_tcp_ses_lock); +- list_for_each_entry(ses, &srvr->smb_ses_list, smb_ses_list) { +- if (ses->Suid == le64_to_cpu(thdr->SessionId)) ++ list_for_each_entry(iter, &srvr->smb_ses_list, smb_ses_list) { ++ if (iter->Suid == le64_to_cpu(thdr->SessionId)) { ++ ses = iter; + break; ++ } + } + spin_unlock(&cifs_tcp_ses_lock); +- if (list_entry_is_head(ses, &srvr->smb_ses_list, +- smb_ses_list)) { ++ if (!ses) { + cifs_dbg(VFS, "no decryption - session id not found\n"); + return 1; + } diff --git a/queue-5.15/cifs-remove-unused-server-parameter-from-calc_smb_size.patch b/queue-5.15/cifs-remove-unused-server-parameter-from-calc_smb_size.patch new file mode 100644 index 0000000000..80b5aeaf18 --- /dev/null +++ b/queue-5.15/cifs-remove-unused-server-parameter-from-calc_smb_size.patch @@ -0,0 +1,151 @@ +From stable+bounces-274535-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:03 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 16:02:14 -0400 +Subject: cifs: remove unused server parameter from calc_smb_size() +To: stable@vger.kernel.org +Cc: Enzo Matsumiya , "Paulo Alcantara (SUSE)" , Steve French , Sasha Levin +Message-ID: <20260714200215.3152449-4-sashal@kernel.org> + +From: Enzo Matsumiya + +[ Upstream commit 68ed14496b032b0c9ef21b38ee45c6c8f3a18ff1 ] + +This parameter is unused by the called function + +Reviewed-by: Paulo Alcantara (SUSE) +Signed-off-by: Enzo Matsumiya +Signed-off-by: Steve French +Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/cifs_debug.c | 2 +- + fs/cifs/cifsglob.h | 2 +- + fs/cifs/cifsproto.h | 2 +- + fs/cifs/misc.c | 2 +- + fs/cifs/netmisc.c | 2 +- + fs/cifs/readdir.c | 6 ++---- + fs/cifs/smb2misc.c | 4 ++-- + fs/cifs/smb2ops.c | 2 +- + fs/cifs/smb2proto.h | 2 +- + 9 files changed, 11 insertions(+), 13 deletions(-) + +--- a/fs/cifs/cifs_debug.c ++++ b/fs/cifs/cifs_debug.c +@@ -42,7 +42,7 @@ void cifs_dump_detail(void *buf, struct + smb->Command, smb->Status.CifsError, + smb->Flags, smb->Flags2, smb->Mid, smb->Pid); + cifs_dbg(VFS, "smb buf %p len %u\n", smb, +- server->ops->calc_smb_size(smb, server)); ++ server->ops->calc_smb_size(smb)); + #endif /* CONFIG_CIFS_DEBUG2 */ + } + +--- a/fs/cifs/cifsglob.h ++++ b/fs/cifs/cifsglob.h +@@ -389,7 +389,7 @@ struct smb_version_operations { + int (*close_dir)(const unsigned int, struct cifs_tcon *, + struct cifs_fid *); + /* calculate a size of SMB message */ +- unsigned int (*calc_smb_size)(void *buf, struct TCP_Server_Info *ptcpi); ++ unsigned int (*calc_smb_size)(void *buf); + /* check for STATUS_PENDING and process the response if yes */ + bool (*is_status_pending)(char *buf, struct TCP_Server_Info *server); + /* check for STATUS_NETWORK_SESSION_EXPIRED */ +--- a/fs/cifs/cifsproto.h ++++ b/fs/cifs/cifsproto.h +@@ -148,7 +148,7 @@ extern int cifs_get_writable_path(struct + extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool); + extern int cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, + struct cifsFileInfo **ret_file); +-extern unsigned int smbCalcSize(void *buf, struct TCP_Server_Info *server); ++extern unsigned int smbCalcSize(void *buf); + extern int decode_negTokenInit(unsigned char *security_blob, int length, + struct TCP_Server_Info *server); + extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); +--- a/fs/cifs/misc.c ++++ b/fs/cifs/misc.c +@@ -357,7 +357,7 @@ checkSMB(char *buf, unsigned int total_r + /* otherwise, there is enough to get to the BCC */ + if (check_smb_hdr(smb)) + return -EIO; +- clc_len = smbCalcSize(smb, server); ++ clc_len = smbCalcSize(smb); + + if (4 + rfclen != total_read) { + cifs_dbg(VFS, "Length read does not match RFC1001 length %d\n", +--- a/fs/cifs/netmisc.c ++++ b/fs/cifs/netmisc.c +@@ -912,7 +912,7 @@ map_and_check_smb_error(struct mid_q_ent + * portion, the number of word parameters and the data portion of the message + */ + unsigned int +-smbCalcSize(void *buf, struct TCP_Server_Info *server) ++smbCalcSize(void *buf) + { + struct smb_hdr *ptr = (struct smb_hdr *)buf; + return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + +--- a/fs/cifs/readdir.c ++++ b/fs/cifs/readdir.c +@@ -808,8 +808,7 @@ find_cifs_entry(const unsigned int xid, + + end_of_smb = cfile->srch_inf.ntwrk_buf_start + + server->ops->calc_smb_size( +- cfile->srch_inf.ntwrk_buf_start, +- server); ++ cfile->srch_inf.ntwrk_buf_start); + + cur_ent = cfile->srch_inf.srch_entries_start; + first_entry_in_buffer = cfile->srch_inf.index_of_last_entry +@@ -1008,8 +1007,7 @@ int cifs_readdir(struct file *file, stru + cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", + num_to_fill, cifsFile->srch_inf.ntwrk_buf_start); + max_len = tcon->ses->server->ops->calc_smb_size( +- cifsFile->srch_inf.ntwrk_buf_start, +- tcon->ses->server); ++ cifsFile->srch_inf.ntwrk_buf_start); + end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; + + tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); +--- a/fs/cifs/smb2misc.c ++++ b/fs/cifs/smb2misc.c +@@ -221,7 +221,7 @@ smb2_check_message(char *buf, unsigned i + } + } + +- calc_len = smb2_calc_size(buf, server); ++ calc_len = smb2_calc_size(buf); + + /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might + * be 0, and not a real miscalculation */ +@@ -403,7 +403,7 @@ smb2_get_data_area_len(int *off, int *le + * portion, the number of word parameters and the data portion of the message. + */ + unsigned int +-smb2_calc_size(void *buf, struct TCP_Server_Info *srvr) ++smb2_calc_size(void *buf) + { + struct smb2_pdu *pdu = (struct smb2_pdu *)buf; + struct smb2_hdr *shdr = &pdu->hdr; +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -374,7 +374,7 @@ smb2_dump_detail(void *buf, struct TCP_S + shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId, + shdr->Id.SyncId.ProcessId); + cifs_server_dbg(VFS, "smb buf %p len %u\n", buf, +- server->ops->calc_smb_size(buf, server)); ++ server->ops->calc_smb_size(buf)); + #endif + } + +--- a/fs/cifs/smb2proto.h ++++ b/fs/cifs/smb2proto.h +@@ -23,7 +23,7 @@ struct smb_rqst; + extern int map_smb2_to_linux_error(char *buf, bool log_err); + extern int smb2_check_message(char *buf, unsigned int length, + struct TCP_Server_Info *server); +-extern unsigned int smb2_calc_size(void *buf, struct TCP_Server_Info *server); ++extern unsigned int smb2_calc_size(void *buf); + extern char *smb2_get_data_area_len(int *off, int *len, + struct smb2_hdr *shdr); + extern __le16 *cifs_convert_path_to_utf16(const char *from, diff --git a/queue-5.15/coresight-etb10-restore-atomic_t-for-shared-reading-state.patch b/queue-5.15/coresight-etb10-restore-atomic_t-for-shared-reading-state.patch new file mode 100644 index 0000000000..80b63a1912 --- /dev/null +++ b/queue-5.15/coresight-etb10-restore-atomic_t-for-shared-reading-state.patch @@ -0,0 +1,123 @@ +From stable+bounces-274998-greg=kroah.com@vger.kernel.org Wed Jul 15 18:57:30 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 12:44:18 -0400 +Subject: coresight: etb10: restore atomic_t for shared reading state +To: stable@vger.kernel.org +Cc: Runyu Xiao , James Clark , Suzuki K Poulose , Sasha Levin +Message-ID: <20260715164418.958997-1-sashal@kernel.org> + +From: Runyu Xiao + +[ Upstream commit fa09f08ede3db3050ae16ae1ed92c902d0cada23 ] + +The etb10 miscdevice uses drvdata->reading as a shared exclusivity gate +for userspace buffer access. etb_open() claims that gate with +local_cmpxchg(), and etb_release() clears it with local_set(). + +That gate is shared per-device state rather than CPU-local state. A +running system can reach it whenever /dev/ is opened, closed, and +reopened by different tasks while the device remains registered, so the +same drvdata->reading variable may be claimed on one CPU and later +cleared on another. + +This code used to use atomic_t for the same gate, but commit +27b10da8fff2 ("coresight: etb10: moving to local atomic operations") +changed it to local_t even though the access pattern remained cross-task +and cross-CPU. Restore atomic_t together with atomic_cmpxchg() and +atomic_set() so the exclusivity gate again uses a primitive intended +for shared state. + +The issue was found on Linux v6.18.21 by our static analysis tool while +scanning surviving local_t-on-shared-state sites, and then manually +reviewed against the live etb10 file-op path. + +It was runtime-validated with a reproducible QEMU no-device KCSAN PoC +that kept the same report-local contract: + + 1. use one shared struct etb_drvdata carrier and its + drvdata->reading gate; + 2. call etb_open() and etb_release() sequentially on that gate to + confirm the original claim/clear path; + 3. bind the open side to CPU0 and the release side to CPU1 for the + same gate to show cross-CPU ownership; + 4. run bound workers that repeatedly race etb_open() and + etb_release() on the same gate until KCSAN reports a target hit. + +The harness recorded: + + L1 passed open=1 release=1 + reading_after_open=1 reading_after_release=0 + L2 passed open_cpu=0 release_cpu=1 + cross_cpu_release=1 reading_after=0 open_ret=0 + +Representative KCSAN excerpt from the no-device validation run: + + BUG: KCSAN: data-race in etb_open.constprop.0.isra.0 [vuln_msv] + + write to 0xffffffffc0003810 of 4 bytes by task 216 on cpu 1: + etb_open.constprop.0.isra.0+0x38/0x80 [vuln_msv] + l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] + kthread+0x17e/0x1c0 + ret_from_fork+0x22/0x30 + + read to 0xffffffffc0003810 of 4 bytes by task 215 on cpu 0: + etb_open.constprop.0.isra.0+0x18/0x80 [vuln_msv] + l3_worker_thread_fn+0x4f/0xf0 [vuln_msv] + kthread+0x17e/0x1c0 + ret_from_fork+0x22/0x30 + + value changed: 0x00000000 -> 0x00000001 + + Reported by Kernel Concurrency Sanitizer on: + CPU: 0 PID: 215 Comm: etb10_l3_a Tainted: G O 6.1.66 #2 + +This no-device harness is not a real ETB10 hardware end-to-end run, but +it preserves the same shared drvdata->reading gate and the same +etb_open()/etb_release() claim/clear contract. No real ETB10 hardware +was available for runtime testing. + +Build-tested with: + make olddefconfig + make -j"$(nproc)" drivers/hwtracing/coresight/coresight-etb10.o + +Fixes: 27b10da8fff2 ("coresight: etb10: moving to local atomic operations") +Cc: stable@vger.kernel.org +Signed-off-by: Runyu Xiao +Reviewed-by: James Clark +Signed-off-by: Suzuki K Poulose +Link: https://lore.kernel.org/r/20260528165201.319452-1-runyu.xiao@seu.edu.cn +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hwtracing/coresight/coresight-etb10.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/hwtracing/coresight/coresight-etb10.c ++++ b/drivers/hwtracing/coresight/coresight-etb10.c +@@ -86,7 +86,7 @@ struct etb_drvdata { + struct coresight_device *csdev; + struct miscdevice miscdev; + spinlock_t spinlock; +- local_t reading; ++ atomic_t reading; + pid_t pid; + u8 *buf; + u32 mode; +@@ -604,7 +604,7 @@ static int etb_open(struct inode *inode, + struct etb_drvdata *drvdata = container_of(file->private_data, + struct etb_drvdata, miscdev); + +- if (local_cmpxchg(&drvdata->reading, 0, 1)) ++ if (atomic_cmpxchg(&drvdata->reading, 0, 1)) + return -EBUSY; + + dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__); +@@ -642,7 +642,7 @@ static int etb_release(struct inode *ino + { + struct etb_drvdata *drvdata = container_of(file->private_data, + struct etb_drvdata, miscdev); +- local_set(&drvdata->reading, 0); ++ atomic_set(&drvdata->reading, 0); + + dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__); + return 0; diff --git a/queue-5.15/crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch b/queue-5.15/crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch new file mode 100644 index 0000000000..7c92a93e17 --- /dev/null +++ b/queue-5.15/crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch @@ -0,0 +1,39 @@ +From stable+bounces-277905-greg=kroah.com@vger.kernel.org Mon Jul 20 17:49:04 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:00:50 -0400 +Subject: crypto: atmel - Drop explicit initialization of struct i2c_device_id::driver_data to 0 +To: stable@vger.kernel.org +Cc: "Uwe Kleine-König" , "Herbert Xu" , "Sasha Levin" +Message-ID: <20260720150051.1918796-2-sashal@kernel.org> + +From: Uwe Kleine-König + +[ Upstream commit d86ad3911a5d4549297ed810ee450e5772fd665f ] + +These drivers don't use the driver_data member of struct i2c_device_id, +so don't explicitly initialize this member. + +This prepares putting driver_data in an anonymous union which requires +either no initialization or named designators. But it's also a nice +cleanup on its own. + +Signed-off-by: Uwe Kleine-König +Signed-off-by: Herbert Xu +Stable-dep-of: ea5e57cc9718 ("crypto: atmel-sha204a - drop hwrng quality reduction for ATSHA204A") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/atmel-ecc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/crypto/atmel-ecc.c ++++ b/drivers/crypto/atmel-ecc.c +@@ -375,7 +375,7 @@ MODULE_DEVICE_TABLE(of, atmel_ecc_dt_ids + #endif + + static const struct i2c_device_id atmel_ecc_id[] = { +- { "atecc508a", 0 }, ++ { "atecc508a" }, + { } + }; + MODULE_DEVICE_TABLE(i2c, atmel_ecc_id); diff --git a/queue-5.15/crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch b/queue-5.15/crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch new file mode 100644 index 0000000000..77bedb23bd --- /dev/null +++ b/queue-5.15/crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch @@ -0,0 +1,101 @@ +From stable+bounces-277907-greg=kroah.com@vger.kernel.org Mon Jul 20 17:49:06 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:00:51 -0400 +Subject: crypto: atmel-sha204a - drop hwrng quality reduction for ATSHA204A +To: stable@vger.kernel.org +Cc: Thorsten Blum , Ard Biesheuvel , Herbert Xu , Sasha Levin +Message-ID: <20260720150051.1918796-3-sashal@kernel.org> + +From: Thorsten Blum + +[ Upstream commit ea5e57cc97185329dcc5ebdcaae7e1500bf0ad0b ] + +Commit 8006aff15516 ("crypto: atmel-sha204a - Set hwrng quality to +lowest possible") reduced the hwrng quality to 1 based on a review by +Bill Cox [1]. However, despite its title, the review only tested the +ATSHA204, not the ATSHA204A. + +In the same thread, Atmel engineer Landon Cox wrote "this behavior has +been eliminated entirely"[2] in the ATSHA204A and "this problem does not +affect the ATECC108 or the ATECC108A (or the ATSHA204A)"[3]. + +According to the official ATSHA204A datasheet [4], the device contains a +high-quality hardware RNG that combines its output with an internal seed +value stored in EEPROM or SRAM to generate random numbers. The device +also implements all security functions using SHA-256, and the driver +uses the chip's Random command in seed-update mode. + +Keep 'quality = 1' for ATSHA204, but drop the explicit hwrng quality +reduction for ATSHA204A and fall back to the hwrng core default. + +[1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html +[2] https://www.metzdowd.com/pipermail/cryptography/2014-December/023852.html +[3] https://www.metzdowd.com/pipermail/cryptography/2014-December/023886.html +[4] https://ww1.microchip.com/downloads/en/DeviceDoc/ATSHA204A-Data-Sheet-40002025A.pdf + +Fixes: 8006aff15516 ("crypto: atmel-sha204a - Set hwrng quality to lowest possible") +Cc: stable@vger.kernel.org +Signed-off-by: Thorsten Blum +Reviewed-by: Ard Biesheuvel +Signed-off-by: Herbert Xu +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/atmel-sha204a.c | 19 ++++++++++++------- + 1 file changed, 12 insertions(+), 7 deletions(-) + +--- a/drivers/crypto/atmel-sha204a.c ++++ b/drivers/crypto/atmel-sha204a.c +@@ -18,6 +18,12 @@ + #include + #include "atmel-i2c.h" + ++/* ++ * According to review by Bill Cox [1], the ATSHA204 has very low entropy. ++ * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html ++ */ ++static const unsigned short atsha204_quality = 1; ++ + static void atmel_sha204a_rng_done(struct atmel_i2c_work_data *work_data, + void *areq, int status) + { +@@ -95,6 +101,7 @@ static int atmel_sha204a_probe(struct i2 + const struct i2c_device_id *id) + { + struct atmel_i2c_client_priv *i2c_priv; ++ const unsigned short *quality; + int ret; + + ret = atmel_i2c_probe(client, id); +@@ -108,11 +115,9 @@ static int atmel_sha204a_probe(struct i2 + i2c_priv->hwrng.name = dev_name(&client->dev); + i2c_priv->hwrng.read = atmel_sha204a_rng_read; + +- /* +- * According to review by Bill Cox [1], this HWRNG has very low entropy. +- * [1] https://www.metzdowd.com/pipermail/cryptography/2014-December/023858.html +- */ +- i2c_priv->hwrng.quality = 1; ++ quality = i2c_get_match_data(client); ++ if (quality) ++ i2c_priv->hwrng.quality = *quality; + + ret = devm_hwrng_register(&client->dev, &i2c_priv->hwrng); + if (ret) +@@ -137,14 +142,14 @@ static int atmel_sha204a_remove(struct i + } + + static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = { +- { .compatible = "atmel,atsha204", }, ++ { .compatible = "atmel,atsha204", .data = &atsha204_quality }, + { .compatible = "atmel,atsha204a", }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids); + + static const struct i2c_device_id atmel_sha204a_id[] = { +- { "atsha204" }, ++ { "atsha204", (kernel_ulong_t)&atsha204_quality }, + { "atsha204a" }, + { /* sentinel */ } + }; diff --git a/queue-5.15/crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch b/queue-5.15/crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch new file mode 100644 index 0000000000..65e8fc29f6 --- /dev/null +++ b/queue-5.15/crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch @@ -0,0 +1,47 @@ +From stable+bounces-277904-greg=kroah.com@vger.kernel.org Mon Jul 20 17:25:13 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:00:49 -0400 +Subject: crypto: atmel-sha204a - Mark OF related data as maybe unused +To: stable@vger.kernel.org +Cc: Krzysztof Kozlowski , Herbert Xu , Sasha Levin +Message-ID: <20260720150051.1918796-1-sashal@kernel.org> + +From: Krzysztof Kozlowski + +[ Upstream commit 75f3d950054389e2556277e42170e37dd97cd872 ] + +The driver can be compile tested with !CONFIG_OF making certain data +unused: + + drivers/crypto/atmel-sha204a.c:129:34: error: ‘atmel_sha204a_dt_ids’ defined but not used [-Werror=unused-const-variable=] + +Signed-off-by: Krzysztof Kozlowski +Signed-off-by: Herbert Xu +Stable-dep-of: ea5e57cc9718 ("crypto: atmel-sha204a - drop hwrng quality reduction for ATSHA204A") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/atmel-sha204a.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/crypto/atmel-sha204a.c ++++ b/drivers/crypto/atmel-sha204a.c +@@ -136,14 +136,16 @@ static int atmel_sha204a_remove(struct i + return 0; + } + +-static const struct of_device_id atmel_sha204a_dt_ids[] = { ++static const struct of_device_id atmel_sha204a_dt_ids[] __maybe_unused = { ++ { .compatible = "atmel,atsha204", }, + { .compatible = "atmel,atsha204a", }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, atmel_sha204a_dt_ids); + + static const struct i2c_device_id atmel_sha204a_id[] = { +- { "atsha204a", 0 }, ++ { "atsha204" }, ++ { "atsha204a" }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(i2c, atmel_sha204a_id); diff --git a/queue-5.15/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch b/queue-5.15/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch new file mode 100644 index 0000000000..0621f7c3cb --- /dev/null +++ b/queue-5.15/crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch @@ -0,0 +1,57 @@ +From stable+bounces-278219-greg=kroah.com@vger.kernel.org Mon Jul 20 22:19:01 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 16:18:53 -0400 +Subject: crypto: qat - fix restarting state leak on allocation failure +To: stable@vger.kernel.org +Cc: Ahsan Atta , Maksim Lukoshkov , Giovanni Cabiddu , Herbert Xu , Sasha Levin +Message-ID: <20260720201853.3239508-1-sashal@kernel.org> + +From: Ahsan Atta + +[ 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 +Co-developed-by: Maksim Lukoshkov +Signed-off-by: Maksim Lukoshkov +Reviewed-by: Giovanni Cabiddu +Signed-off-by: Herbert Xu +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/qat/qat_common/adf_aer.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/drivers/crypto/qat/qat_common/adf_aer.c ++++ b/drivers/crypto/qat/qat_common/adf_aer.c +@@ -116,13 +116,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-5.15/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch b/queue-5.15/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch new file mode 100644 index 0000000000..bd46f9bf47 --- /dev/null +++ b/queue-5.15/crypto-qat-fix-vf2pf-work-teardown-race-in-adf_disable_sriov.patch @@ -0,0 +1,230 @@ +From stable+bounces-274714-greg=kroah.com@vger.kernel.org Wed Jul 15 05:16:11 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 23:16:01 -0400 +Subject: crypto: qat - fix VF2PF work teardown race in adf_disable_sriov() +To: stable@vger.kernel.org +Cc: Giovanni Cabiddu , Ahsan Atta , Herbert Xu , Sasha Levin +Message-ID: <20260715031602.247772-1-sashal@kernel.org> + +From: Giovanni Cabiddu + +[ 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 +Reviewed-by: Ahsan Atta +Signed-off-by: Herbert Xu +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/qat/qat_common/adf_accel_devices.h | 2 + + drivers/crypto/qat/qat_common/adf_common_drv.h | 4 +++ + drivers/crypto/qat/qat_common/adf_isr.c | 18 ++++++++++++++ + drivers/crypto/qat/qat_common/adf_pf2vf_msg.c | 27 ++++++++++++++++++++++ + drivers/crypto/qat/qat_common/adf_sriov.c | 27 ++++++++++++++++++---- + 5 files changed, 74 insertions(+), 4 deletions(-) + +--- a/drivers/crypto/qat/qat_common/adf_accel_devices.h ++++ b/drivers/crypto/qat/qat_common/adf_accel_devices.h +@@ -248,6 +248,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/qat/qat_common/adf_common_drv.h ++++ b/drivers/crypto/qat/qat_common/adf_common_drv.h +@@ -120,6 +120,7 @@ void qat_asym_algs_unregister(void); + + 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); + +@@ -189,6 +190,9 @@ void adf_disable_vf2pf_interrupts_irq(st + u32 vf_mask); + 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); + void adf_enable_pf2vf_interrupts(struct adf_accel_dev *accel_dev); + void adf_disable_pf2vf_interrupts(struct adf_accel_dev *accel_dev); + void adf_schedule_vf2pf_handler(struct adf_accel_vf_info *vf_info); +--- a/drivers/crypto/qat/qat_common/adf_isr.c ++++ b/drivers/crypto/qat/qat_common/adf_isr.c +@@ -135,6 +135,24 @@ 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 = accel_dev->hw_device; ++ struct msix_entry *msixe = pci_dev_info->msix_entries.entries; ++ u32 num_entries = pci_dev_info->msix_entries.num_entries; ++ u32 irq_idx; ++ ++ if (!test_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status) || !msixe) ++ return; ++ ++ irq_idx = num_entries > 1 ? hw_data->num_banks : 0; ++ if (irq_idx >= num_entries) ++ return; ++ ++ synchronize_irq(msixe[irq_idx].vector); ++} ++ + static int adf_request_irqs(struct adf_accel_dev *accel_dev) + { + struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; +--- a/drivers/crypto/qat/qat_common/adf_pf2vf_msg.c ++++ b/drivers/crypto/qat/qat_common/adf_pf2vf_msg.c +@@ -40,6 +40,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)) ++ __adf_enable_vf2pf_interrupts(accel_dev, 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) ++{ ++ 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); + __adf_enable_vf2pf_interrupts(accel_dev, vf_mask); + spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); + } +@@ -84,6 +101,16 @@ void adf_disable_vf2pf_interrupts_irq(st + spin_unlock(&accel_dev->pf.vf2pf_ints_lock); + } + ++void adf_disable_all_vf2pf_interrupts(struct adf_accel_dev *accel_dev) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&accel_dev->pf.vf2pf_ints_lock, flags); ++ WRITE_ONCE(accel_dev->pf.vf2pf_disabled, true); ++ __adf_disable_vf2pf_interrupts(accel_dev, GENMASK(31, 0)); ++ spin_unlock_irqrestore(&accel_dev->pf.vf2pf_ints_lock, flags); ++} ++ + static int __adf_iov_putmsg(struct adf_accel_dev *accel_dev, u32 msg, u8 vf_nr) + { + struct adf_accel_pci *pci_info = &accel_dev->accel_pci_dev; +--- a/drivers/crypto/qat/qat_common/adf_sriov.c ++++ b/drivers/crypto/qat/qat_common/adf_sriov.c +@@ -19,15 +19,26 @@ static void adf_iov_send_resp(struct wor + { + struct adf_pf2vf_resp *pf2vf_resp = + container_of(work, struct adf_pf2vf_resp, pf2vf_resp_work); ++ struct adf_accel_vf_info *vf_info = pf2vf_resp->vf_info; ++ struct adf_accel_dev *accel_dev = vf_info->accel_dev; + +- adf_vf2pf_req_hndl(pf2vf_resp->vf_info); ++ if (READ_ONCE(accel_dev->pf.vf2pf_disabled)) ++ goto out; ++ ++ adf_vf2pf_req_hndl(vf_info); ++ ++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; +@@ -37,6 +48,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); +@@ -63,7 +80,7 @@ static int adf_enable_sriov(struct adf_a + + /* Enable VF to PF interrupts for all VFs */ + if (hw_data->get_pf2vf_offset) +- adf_enable_vf2pf_interrupts(accel_dev, BIT_ULL(totalvfs) - 1); ++ adf_enable_all_vf2pf_interrupts(accel_dev, totalvfs); + + /* + * Due to the hardware design, when SR-IOV and the ring arbiter +@@ -97,9 +114,11 @@ void adf_disable_sriov(struct adf_accel_ + + pci_disable_sriov(accel_to_pci_dev(accel_dev)); + +- /* Disable VF to PF interrupts */ ++ /* Block VF2PF work and disable VF to PF interrupts */ + if (hw_data->get_pf2vf_offset) +- adf_disable_vf2pf_interrupts(accel_dev, GENMASK(31, 0)); ++ 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-5.15/crypto-qat-validate-rsa-crt-component-lengths.patch b/queue-5.15/crypto-qat-validate-rsa-crt-component-lengths.patch new file mode 100644 index 0000000000..6c03753fea --- /dev/null +++ b/queue-5.15/crypto-qat-validate-rsa-crt-component-lengths.patch @@ -0,0 +1,87 @@ +From stable+bounces-278283-greg=kroah.com@vger.kernel.org Tue Jul 21 02:45:14 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 20:45:08 -0400 +Subject: crypto: qat - validate RSA CRT component lengths +To: stable@vger.kernel.org +Cc: Giovanni Cabiddu , Ahsan Atta , Laurent M Coquerel , Herbert Xu , Sasha Levin +Message-ID: <20260721004508.3421467-1-sashal@kernel.org> + +From: Giovanni Cabiddu + +[ Upstream commit b3ac78756588059729b9195fcc9f4b37d54057a5 ] + +The generic RSA key parser (rsa_helper.c) bounds each CRT component (p, +q, dp, dq, qinv) by the modulus size n_sz, but qat_rsa_setkey_crt() +allocates half-size DMA buffers (key_sz / 2) and right-aligns each +component with: + + memcpy(dst + half_key_sz - len, src, len) + +When a CRT component is larger than half_key_sz the subtraction +underflows and memcpy writes past the DMA buffer, causing memory +corruption. + +Add a len > half_key_sz check next to the existing !len check for each +of the five CRT components so the driver falls back to the non-CRT path +instead of writing out of bounds. + +Fixes: 879f77e9071f ("crypto: qat - Add RSA CRT mode") +Cc: stable@vger.kernel.org +Signed-off-by: Giovanni Cabiddu +Reviewed-by: Ahsan Atta +Reviewed-by: Laurent M Coquerel +Tested-by: Laurent M Coquerel +Signed-off-by: Herbert Xu +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/qat/qat_common/qat_asym_algs.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +--- a/drivers/crypto/qat/qat_common/qat_asym_algs.c ++++ b/drivers/crypto/qat/qat_common/qat_asym_algs.c +@@ -1032,7 +1032,7 @@ static void qat_rsa_setkey_crt(struct qa + ptr = rsa_key->p; + len = rsa_key->p_sz; + qat_rsa_drop_leading_zeros(&ptr, &len); +- if (!len) ++ if (!len || len > half_key_sz) + goto err; + ctx->p = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_p, GFP_KERNEL); + if (!ctx->p) +@@ -1043,7 +1043,7 @@ static void qat_rsa_setkey_crt(struct qa + ptr = rsa_key->q; + len = rsa_key->q_sz; + qat_rsa_drop_leading_zeros(&ptr, &len); +- if (!len) ++ if (!len || len > half_key_sz) + goto free_p; + ctx->q = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_q, GFP_KERNEL); + if (!ctx->q) +@@ -1054,7 +1054,7 @@ static void qat_rsa_setkey_crt(struct qa + ptr = rsa_key->dp; + len = rsa_key->dp_sz; + qat_rsa_drop_leading_zeros(&ptr, &len); +- if (!len) ++ if (!len || len > half_key_sz) + goto free_q; + ctx->dp = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dp, + GFP_KERNEL); +@@ -1066,7 +1066,7 @@ static void qat_rsa_setkey_crt(struct qa + ptr = rsa_key->dq; + len = rsa_key->dq_sz; + qat_rsa_drop_leading_zeros(&ptr, &len); +- if (!len) ++ if (!len || len > half_key_sz) + goto free_dp; + ctx->dq = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_dq, + GFP_KERNEL); +@@ -1078,7 +1078,7 @@ static void qat_rsa_setkey_crt(struct qa + ptr = rsa_key->qinv; + len = rsa_key->qinv_sz; + qat_rsa_drop_leading_zeros(&ptr, &len); +- if (!len) ++ if (!len || len > half_key_sz) + goto free_dq; + ctx->qinv = dma_alloc_coherent(dev, half_key_sz, &ctx->dma_qinv, + GFP_KERNEL); diff --git a/queue-5.15/drm-i2c-sil164-drop-no-op-remove-function.patch b/queue-5.15/drm-i2c-sil164-drop-no-op-remove-function.patch new file mode 100644 index 0000000000..a33fd6711a --- /dev/null +++ b/queue-5.15/drm-i2c-sil164-drop-no-op-remove-function.patch @@ -0,0 +1,47 @@ +From stable+bounces-277528-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:52 2026 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:25:33 -0400 +Subject: drm/i2c/sil164: Drop no-op remove function +To: stable@vger.kernel.org +Cc: "Uwe Kleine-König" , "Wolfram Sang" , "Sasha Levin" +Message-ID: <20260720022537.2927284-1-sashal@kernel.org> + +From: Uwe Kleine-König + +[ Upstream commit eb09882d64807c24228ed1d005dbbae70860ca3f ] + +A remove callback that just returns 0 is equivalent to no callback at all +as can be seen in i2c_device_remove(). So simplify accordingly. + +Signed-off-by: Uwe Kleine-König +Signed-off-by: Wolfram Sang +Stable-dep-of: 1f0bdc2884b6 ("usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/i2c/sil164_drv.c | 7 ------- + 1 file changed, 7 deletions(-) + +--- a/drivers/gpu/drm/i2c/sil164_drv.c ++++ b/drivers/gpu/drm/i2c/sil164_drv.c +@@ -370,12 +370,6 @@ sil164_probe(struct i2c_client *client, + return 0; + } + +-static int +-sil164_remove(struct i2c_client *client) +-{ +- return 0; +-} +- + static struct i2c_client * + sil164_detect_slave(struct i2c_client *client) + { +@@ -427,7 +421,6 @@ MODULE_DEVICE_TABLE(i2c, sil164_ids); + static struct drm_i2c_encoder_driver sil164_driver = { + .i2c_driver = { + .probe = sil164_probe, +- .remove = sil164_remove, + .driver = { + .name = "sil164", + }, diff --git a/queue-5.15/gpio-pca953x-make-platform-teardown-callback-return-void.patch b/queue-5.15/gpio-pca953x-make-platform-teardown-callback-return-void.patch new file mode 100644 index 0000000000..341b1a3b83 --- /dev/null +++ b/queue-5.15/gpio-pca953x-make-platform-teardown-callback-return-void.patch @@ -0,0 +1,106 @@ +From stable+bounces-277531-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:48 2026 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:25:36 -0400 +Subject: gpio: pca953x: Make platform teardown callback return void +To: stable@vger.kernel.org +Cc: "Uwe Kleine-König" , "Andy Shevchenko" , "Bartosz Golaszewski" , "Wolfram Sang" , "Sasha Levin" +Message-ID: <20260720022537.2927284-4-sashal@kernel.org> + +From: Uwe Kleine-König + +[ Upstream commit 6a8f359c3132e4f51bdb263ad74ec632c65e55fd ] + +All platforms that provide a teardown callback return 0. New users are +supposed to not make use of platform support, so there is no +functionality lost. + +This patch is a preparation for making i2c remove callbacks return void. + +Reviewed-by: Andy Shevchenko +Signed-off-by: Uwe Kleine-König +Acked-by: Bartosz Golaszewski +Signed-off-by: Wolfram Sang +Stable-dep-of: 1f0bdc2884b6 ("usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm/mach-davinci/board-da850-evm.c | 12 ++++-------- + drivers/gpio/gpio-pca953x.c | 11 +++-------- + include/linux/platform_data/pca953x.h | 2 +- + 3 files changed, 8 insertions(+), 17 deletions(-) + +--- a/arch/arm/mach-davinci/board-da850-evm.c ++++ b/arch/arm/mach-davinci/board-da850-evm.c +@@ -517,8 +517,8 @@ exp_setup_sela_fail: + return ret; + } + +-static int da850_evm_ui_expander_teardown(struct i2c_client *client, +- unsigned gpio, unsigned ngpio, void *c) ++static void da850_evm_ui_expander_teardown(struct i2c_client *client, ++ unsigned gpio, unsigned ngpio, void *c) + { + platform_device_unregister(&da850_evm_ui_keys_device); + +@@ -530,8 +530,6 @@ static int da850_evm_ui_expander_teardow + gpio_free(gpio + DA850_EVM_UI_EXP_SEL_C); + gpio_free(gpio + DA850_EVM_UI_EXP_SEL_B); + gpio_free(gpio + DA850_EVM_UI_EXP_SEL_A); +- +- return 0; + } + + /* assign the baseboard expander's GPIOs after the UI board's */ +@@ -698,13 +696,11 @@ io_exp_setup_sw_fail: + return ret; + } + +-static int da850_evm_bb_expander_teardown(struct i2c_client *client, +- unsigned gpio, unsigned ngpio, void *c) ++static void da850_evm_bb_expander_teardown(struct i2c_client *client, ++ unsigned gpio, unsigned ngpio, void *c) + { + platform_device_unregister(&da850_evm_bb_leds_device); + platform_device_unregister(&da850_evm_bb_keys_device); +- +- return 0; + } + + static struct pca953x_platform_data da850_evm_ui_expander_info = { +--- a/drivers/gpio/gpio-pca953x.c ++++ b/drivers/gpio/gpio-pca953x.c +@@ -1074,20 +1074,15 @@ static int pca953x_remove(struct i2c_cli + { + struct pca953x_platform_data *pdata = dev_get_platdata(&client->dev); + struct pca953x_chip *chip = i2c_get_clientdata(client); +- int ret; + + if (pdata && pdata->teardown) { +- ret = pdata->teardown(client, chip->gpio_chip.base, +- chip->gpio_chip.ngpio, pdata->context); +- if (ret < 0) +- dev_err(&client->dev, "teardown failed, %d\n", ret); +- } else { +- ret = 0; ++ pdata->teardown(client, chip->gpio_chip.base, ++ chip->gpio_chip.ngpio, pdata->context); + } + + regulator_disable(chip->regulator); + +- return ret; ++ return 0; + } + + #ifdef CONFIG_PM_SLEEP +--- a/include/linux/platform_data/pca953x.h ++++ b/include/linux/platform_data/pca953x.h +@@ -22,7 +22,7 @@ struct pca953x_platform_data { + int (*setup)(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *context); +- int (*teardown)(struct i2c_client *client, ++ void (*teardown)(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *context); + const char *const *names; diff --git a/queue-5.15/gpio-sch-use-new-gpio-line-value-setter-callbacks.patch b/queue-5.15/gpio-sch-use-new-gpio-line-value-setter-callbacks.patch new file mode 100644 index 0000000000..8779097544 --- /dev/null +++ b/queue-5.15/gpio-sch-use-new-gpio-line-value-setter-callbacks.patch @@ -0,0 +1,65 @@ +From stable+bounces-275010-greg=kroah.com@vger.kernel.org Wed Jul 15 19:58:03 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 13:55:23 -0400 +Subject: gpio: sch: use new GPIO line value setter callbacks +To: stable@vger.kernel.org +Cc: Bartosz Golaszewski , Andy Shevchenko , Sasha Levin +Message-ID: <20260715175524.1011791-1-sashal@kernel.org> + +From: Bartosz Golaszewski + +[ Upstream commit 883c7eb2c4a9e143b2662ba754f9c16fb31adced ] + +struct gpio_chip now has callbacks for setting line values that return +an integer, allowing to indicate failures. Convert the driver to using +them. + +Acked-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20250625-gpiochip-set-rv-gpio-round2-v1-3-bc110a3b52ff@linaro.org +Signed-off-by: Bartosz Golaszewski +Stable-dep-of: 286533cb14a3 ("gpio: sch: use raw_spinlock_t in the irq startup path") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpio/gpio-sch.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/drivers/gpio/gpio-sch.c ++++ b/drivers/gpio/gpio-sch.c +@@ -116,7 +116,7 @@ static int sch_gpio_get(struct gpio_chip + return sch_gpio_reg_get(sch, gpio_num, GLV); + } + +-static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) ++static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) + { + struct sch_gpio *sch = gpiochip_get_data(gc); + unsigned long flags; +@@ -124,6 +124,8 @@ static void sch_gpio_set(struct gpio_chi + spin_lock_irqsave(&sch->lock, flags); + sch_gpio_reg_set(sch, gpio_num, GLV, val); + spin_unlock_irqrestore(&sch->lock, flags); ++ ++ return 0; + } + + static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, +@@ -145,8 +147,7 @@ static int sch_gpio_direction_out(struct + * But we cannot prevent a short low pulse if direction is set to high + * and an external pull-up is connected. + */ +- sch_gpio_set(gc, gpio_num, val); +- return 0; ++ return sch_gpio_set(gc, gpio_num, val); + } + + static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) +@@ -165,7 +166,7 @@ static const struct gpio_chip sch_gpio_c + .direction_input = sch_gpio_direction_in, + .get = sch_gpio_get, + .direction_output = sch_gpio_direction_out, +- .set = sch_gpio_set, ++ .set_rv = sch_gpio_set, + .get_direction = sch_gpio_get_direction, + }; + diff --git a/queue-5.15/gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch b/queue-5.15/gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch new file mode 100644 index 0000000000..336c2138c3 --- /dev/null +++ b/queue-5.15/gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch @@ -0,0 +1,190 @@ +From stable+bounces-275011-greg=kroah.com@vger.kernel.org Wed Jul 15 19:58:40 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 13:55:24 -0400 +Subject: gpio: sch: use raw_spinlock_t in the irq startup path +To: stable@vger.kernel.org +Cc: Runyu Xiao , Sebastian Andrzej Siewior , Andy Shevchenko , Bartosz Golaszewski , Sasha Levin +Message-ID: <20260715175524.1011791-2-sashal@kernel.org> + +From: Runyu Xiao + +[ Upstream commit 286533cb14a3c8a8bd39ff64ea2fc8e1aa0f638b ] + +sch_irq_unmask() enables the GPIO IRQ and then updates the controller +state through sch_irq_mask_unmask(), which takes sch->lock with +spin_lock_irqsave(). The callback can be reached from irq_startup() +while setting up a requested IRQ. That path is not sleepable, but on +PREEMPT_RT a regular spinlock_t becomes a sleeping lock. + +This issue was found by our static analysis tool and then manually +reviewed against the current tree. + +The grounded PoC kept the request_threaded_irq() -> __setup_irq() -> +irq_startup() -> sch_irq_unmask() -> sch_irq_mask_unmask() carrier and +used the original spin_lock_irqsave(&sch->lock) edge. Lockdep reported: + + BUG: sleeping function called from invalid context + hardirqs last disabled at ... __setup_irq.constprop.0 ... [vuln_msv] + sch_rt_spin_lock_irqsave+0x1c/0x30 [vuln_msv] + sch_irq_mask_unmask.constprop.0+0x31/0x70 [vuln_msv] + __setup_irq.constprop.0+0xd/0x30 [vuln_msv] + +Convert the SCH controller lock to raw_spinlock_t. The same lock is +also used by the GPIO direction and value callbacks, but those critical +sections only update MMIO-backed GPIO registers and do not contain +sleepable operations. Keeping this register lock non-sleeping is +therefore appropriate for the irqchip callbacks and does not change the +GPIO-side locking contract. + +Fixes: 7a81638485c1 ("gpio: sch: Add edge event support") +Cc: stable@vger.kernel.org +Signed-off-by: Runyu Xiao +Reviewed-by: Sebastian Andrzej Siewior +Reviewed-by: Andy Shevchenko +Link: https://patch.msgid.link/20260617154035.1199948-2-runyu.xiao@seu.edu.cn +Signed-off-by: Bartosz Golaszewski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpio/gpio-sch.c | 41 ++++++++++++++++++++--------------------- + 1 file changed, 20 insertions(+), 21 deletions(-) + +--- a/drivers/gpio/gpio-sch.c ++++ b/drivers/gpio/gpio-sch.c +@@ -39,7 +39,7 @@ + struct sch_gpio { + struct gpio_chip chip; + struct irq_chip irqchip; +- spinlock_t lock; ++ raw_spinlock_t lock; + unsigned short iobase; + unsigned short resume_base; + +@@ -103,9 +103,9 @@ static int sch_gpio_direction_in(struct + struct sch_gpio *sch = gpiochip_get_data(gc); + unsigned long flags; + +- spin_lock_irqsave(&sch->lock, flags); ++ raw_spin_lock_irqsave(&sch->lock, flags); + sch_gpio_reg_set(sch, gpio_num, GIO, 1); +- spin_unlock_irqrestore(&sch->lock, flags); ++ raw_spin_unlock_irqrestore(&sch->lock, flags); + return 0; + } + +@@ -116,16 +116,14 @@ static int sch_gpio_get(struct gpio_chip + return sch_gpio_reg_get(sch, gpio_num, GLV); + } + +-static int sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) ++static void sch_gpio_set(struct gpio_chip *gc, unsigned int gpio_num, int val) + { + struct sch_gpio *sch = gpiochip_get_data(gc); + unsigned long flags; + +- spin_lock_irqsave(&sch->lock, flags); ++ raw_spin_lock_irqsave(&sch->lock, flags); + sch_gpio_reg_set(sch, gpio_num, GLV, val); +- spin_unlock_irqrestore(&sch->lock, flags); +- +- return 0; ++ raw_spin_unlock_irqrestore(&sch->lock, flags); + } + + static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned int gpio_num, +@@ -134,9 +132,9 @@ static int sch_gpio_direction_out(struct + struct sch_gpio *sch = gpiochip_get_data(gc); + unsigned long flags; + +- spin_lock_irqsave(&sch->lock, flags); ++ raw_spin_lock_irqsave(&sch->lock, flags); + sch_gpio_reg_set(sch, gpio_num, GIO, 0); +- spin_unlock_irqrestore(&sch->lock, flags); ++ raw_spin_unlock_irqrestore(&sch->lock, flags); + + /* + * according to the datasheet, writing to the level register has no +@@ -147,7 +145,8 @@ static int sch_gpio_direction_out(struct + * But we cannot prevent a short low pulse if direction is set to high + * and an external pull-up is connected. + */ +- return sch_gpio_set(gc, gpio_num, val); ++ sch_gpio_set(gc, gpio_num, val); ++ return 0; + } + + static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio_num) +@@ -166,7 +165,7 @@ static const struct gpio_chip sch_gpio_c + .direction_input = sch_gpio_direction_in, + .get = sch_gpio_get, + .direction_output = sch_gpio_direction_out, +- .set_rv = sch_gpio_set, ++ .set = sch_gpio_set, + .get_direction = sch_gpio_get_direction, + }; + +@@ -195,14 +194,14 @@ static int sch_irq_type(struct irq_data + return -EINVAL; + } + +- spin_lock_irqsave(&sch->lock, flags); ++ raw_spin_lock_irqsave(&sch->lock, flags); + + sch_gpio_reg_set(sch, gpio_num, GTPE, rising); + sch_gpio_reg_set(sch, gpio_num, GTNE, falling); + + irq_set_handler_locked(d, handle_edge_irq); + +- spin_unlock_irqrestore(&sch->lock, flags); ++ raw_spin_unlock_irqrestore(&sch->lock, flags); + + return 0; + } +@@ -214,9 +213,9 @@ static void sch_irq_ack(struct irq_data + irq_hw_number_t gpio_num = irqd_to_hwirq(d); + unsigned long flags; + +- spin_lock_irqsave(&sch->lock, flags); ++ raw_spin_lock_irqsave(&sch->lock, flags); + sch_gpio_reg_set(sch, gpio_num, GTS, 1); +- spin_unlock_irqrestore(&sch->lock, flags); ++ raw_spin_unlock_irqrestore(&sch->lock, flags); + } + + static void sch_irq_mask_unmask(struct irq_data *d, int val) +@@ -226,9 +225,9 @@ static void sch_irq_mask_unmask(struct i + irq_hw_number_t gpio_num = irqd_to_hwirq(d); + unsigned long flags; + +- spin_lock_irqsave(&sch->lock, flags); ++ raw_spin_lock_irqsave(&sch->lock, flags); + sch_gpio_reg_set(sch, gpio_num, GGPE, val); +- spin_unlock_irqrestore(&sch->lock, flags); ++ raw_spin_unlock_irqrestore(&sch->lock, flags); + } + + static void sch_irq_mask(struct irq_data *d) +@@ -251,12 +250,12 @@ static u32 sch_gpio_gpe_handler(acpi_han + int offset; + u32 ret; + +- spin_lock_irqsave(&sch->lock, flags); ++ raw_spin_lock_irqsave(&sch->lock, flags); + + core_status = inl(sch->iobase + CORE_BANK_OFFSET + GTS); + resume_status = inl(sch->iobase + RESUME_BANK_OFFSET + GTS); + +- spin_unlock_irqrestore(&sch->lock, flags); ++ raw_spin_unlock_irqrestore(&sch->lock, flags); + + pending = (resume_status << sch->resume_base) | core_status; + for_each_set_bit(offset, &pending, sch->chip.ngpio) +@@ -322,7 +321,7 @@ static int sch_gpio_probe(struct platfor + pdev->name)) + return -EBUSY; + +- spin_lock_init(&sch->lock); ++ raw_spin_lock_init(&sch->lock); + sch->iobase = res->start; + sch->chip = sch_gpio_chip; + sch->chip.label = dev_name(&pdev->dev); diff --git a/queue-5.15/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch b/queue-5.15/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch new file mode 100644 index 0000000000..e04f8b0e92 --- /dev/null +++ b/queue-5.15/hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch @@ -0,0 +1,69 @@ +From stable+bounces-277296-greg=kroah.com@vger.kernel.org Sat Jul 18 13:44:37 2026 +From: Sasha Levin +Date: Sat, 18 Jul 2026 07:44:27 -0400 +Subject: hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length +To: stable@vger.kernel.org +Cc: Tristan Madani , syzbot+6df204b70bf3261691c5@syzkaller.appspotmail.com, syzbot+e76bf3d19b85350571ac@syzkaller.appspotmail.com, Viacheslav Dubeyko , Sasha Levin +Message-ID: <20260718114427.3045393-2-sashal@kernel.org> + +From: Tristan Madani + +[ 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 +Reviewed-by: Viacheslav Dubeyko +Signed-off-by: Viacheslav Dubeyko +Link: https://lore.kernel.org/r/20260505111300.3592757-2-tristmd@gmail.com +Signed-off-by: Viacheslav Dubeyko +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -611,7 +611,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-5.15/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch b/queue-5.15/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch new file mode 100644 index 0000000000..53b7fecf30 --- /dev/null +++ b/queue-5.15/hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch @@ -0,0 +1,958 @@ +From stable+bounces-277297-greg=kroah.com@vger.kernel.org Sat Jul 18 13:44:43 2026 +From: Sasha Levin +Date: Sat, 18 Jul 2026 07:44:26 -0400 +Subject: hfs/hfsplus: prevent getting negative values of offset/length +To: stable@vger.kernel.org +Cc: Viacheslav Dubeyko , John Paul Adrian Glaubitz , Yangtao Li , linux-fsdevel@vger.kernel.org, Sasha Levin +Message-ID: <20260718114427.3045393-1-sashal@kernel.org> + +From: Viacheslav Dubeyko + +[ 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] +[ 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] +[ 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 +cc: John Paul Adrian Glaubitz +cc: Yangtao Li +cc: linux-fsdevel@vger.kernel.org +Link: https://lore.kernel.org/r/20251002200020.2578311-1-slava@dubeyko.com +Signed-off-by: Viacheslav Dubeyko +Stable-dep-of: 966cb76fb285 ("hfs/hfsplus: fix u32 overflow in check_and_correct_requested_length") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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 | 85 ++++++++++++++++++++++++++++-------------------- + fs/hfs/inode.c | 2 - + 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, 168 insertions(+), 152 deletions(-) + +--- a/fs/hfs/bfind.c ++++ b/fs/hfs/bfind.c +@@ -164,7 +164,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; + void *vaddr; + + memset(buf, 0, len); +@@ -73,7 +73,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; +@@ -89,7 +89,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); + + vaddr = kmap_atomic(page); + memcpy(buf + bytes_read, vaddr + off, bytes_to_read); +@@ -100,7 +100,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... +@@ -108,7 +108,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... +@@ -116,10 +116,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 || +@@ -130,14 +130,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; + +@@ -147,7 +147,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; +@@ -163,20 +163,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; + +@@ -186,7 +186,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; +@@ -202,8 +202,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; + +@@ -225,7 +225,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 +@@ -221,7 +221,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 +@@ -171,71 +171,86 @@ 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_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_ext_keycmp(const btree_key *key1, const btree_key *key2); ++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; + +-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 user_namespace *, struct dentry *, +- struct iattr *); ++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 user_namespace *mnt_userns, ++ 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 *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 +@@ -50,7 +50,7 @@ static void hfs_write_failed(struct addr + } + + static int hfs_write_begin(struct file *file, struct address_space *mapping, +- loff_t pos, unsigned len, unsigned flags, ++ loff_t pos, unsigned int len, unsigned flags, + struct page **pagep, 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,19 +45,19 @@ 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(buf, kmap(*pagep) + off, l); + kunmap(*pagep); + + while ((len -= l) != 0) { + buf += l; +- l = min_t(int, len, PAGE_SIZE); ++ l = min_t(u32, len, PAGE_SIZE); + memcpy(buf, kmap(*++pagep), l); + kunmap(*pagep); + } + } + +-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... */ +@@ -65,7 +65,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... */ +@@ -73,10 +73,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 || +@@ -88,17 +88,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; +@@ -106,7 +106,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; +@@ -118,31 +118,31 @@ 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(kmap(*pagep) + off, buf, l); + set_page_dirty(*pagep); + kunmap(*pagep); + + while ((len -= l) != 0) { + buf += l; +- l = min_t(int, len, PAGE_SIZE); ++ l = min_t(u32, len, PAGE_SIZE); + memcpy(kmap(*++pagep), buf, l); + set_page_dirty(*pagep); + kunmap(*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; +@@ -150,7 +150,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; +@@ -162,24 +162,24 @@ 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); + memset(kmap(*pagep) + off, 0, l); + set_page_dirty(*pagep); + kunmap(*pagep); + + while ((len -= l) != 0) { +- l = min_t(int, len, PAGE_SIZE); ++ l = min_t(u32, len, PAGE_SIZE); + memset(kmap(*++pagep), 0, l); + set_page_dirty(*pagep); + kunmap(*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(BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len); + if (!len) +@@ -196,14 +196,14 @@ 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(kmap(*dst_page) + src, kmap(*src_page) + src, l); + kunmap(*src_page); + set_page_dirty(*dst_page); + kunmap(*dst_page); + + while ((len -= l) != 0) { +- l = min_t(int, len, PAGE_SIZE); ++ l = min_t(u32, len, PAGE_SIZE); + memcpy(kmap(*++dst_page), kmap(*++src_page), l); + kunmap(*src_page); + set_page_dirty(*dst_page); +@@ -237,10 +237,10 @@ 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; +- int l; ++ u32 l; + + hfs_dbg(BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len); + if (!len) +@@ -309,7 +309,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); + memmove(kmap(*dst_page) + src, + kmap(*src_page) + src, l); + kunmap(*src_page); +@@ -317,7 +317,7 @@ void hfs_bnode_move(struct hfs_bnode *no + kunmap(*dst_page); + + while ((len -= l) != 0) { +- l = min_t(int, len, PAGE_SIZE); ++ l = min_t(u32, len, PAGE_SIZE); + memmove(kmap(*++dst_page), + kmap(*++src_page), l); + kunmap(*src_page); +--- 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 +@@ -343,7 +343,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 +@@ -388,21 +388,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); +@@ -417,7 +417,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 */ +@@ -430,7 +430,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 */ +@@ -586,14 +586,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); + } +@@ -602,7 +602,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; + +@@ -612,12 +612,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-5.15/hid-add-haptics-page-defines.patch b/queue-5.15/hid-add-haptics-page-defines.patch new file mode 100644 index 0000000000..33e051d9ef --- /dev/null +++ b/queue-5.15/hid-add-haptics-page-defines.patch @@ -0,0 +1,78 @@ +From stable+bounces-277074-greg=kroah.com@vger.kernel.org Fri Jul 17 16:34:56 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 10:34:46 -0400 +Subject: HID: add haptics page defines +To: stable@vger.kernel.org +Cc: Angela Czubak , Jonathan Denose , Benjamin Tissoires , Sasha Levin +Message-ID: <20260717143447.1877787-1-sashal@kernel.org> + +From: Angela Czubak + +[ Upstream commit 5e0ae59159e3a07391a35865bb79ff335473fa79 ] + +Introduce haptic usages as defined in HID Usage Tables specification. +Add HID units for newton and gram. + +Signed-off-by: Angela Czubak +Co-developed-by: Jonathan Denose +Signed-off-by: Jonathan Denose +Signed-off-by: Benjamin Tissoires +Stable-dep-of: 8813b0612275 ("HID: multitouch: fix out-of-bounds bit access on mt_io_flags") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/hid.h | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +--- a/include/linux/hid.h ++++ b/include/linux/hid.h +@@ -153,6 +153,7 @@ struct hid_item { + #define HID_UP_TELEPHONY 0x000b0000 + #define HID_UP_CONSUMER 0x000c0000 + #define HID_UP_DIGITIZER 0x000d0000 ++#define HID_UP_HAPTIC 0x000e0000 + #define HID_UP_PID 0x000f0000 + #define HID_UP_BATTERY 0x00850000 + #define HID_UP_HPVENDOR 0xff7f0000 +@@ -301,6 +302,28 @@ struct hid_item { + #define HID_DG_TOOLSERIALNUMBER 0x000d005b + #define HID_DG_LATENCYMODE 0x000d0060 + ++#define HID_HP_SIMPLECONTROLLER 0x000e0001 ++#define HID_HP_WAVEFORMLIST 0x000e0010 ++#define HID_HP_DURATIONLIST 0x000e0011 ++#define HID_HP_AUTOTRIGGER 0x000e0020 ++#define HID_HP_MANUALTRIGGER 0x000e0021 ++#define HID_HP_AUTOTRIGGERASSOCIATEDCONTROL 0x000e0022 ++#define HID_HP_INTENSITY 0x000e0023 ++#define HID_HP_REPEATCOUNT 0x000e0024 ++#define HID_HP_RETRIGGERPERIOD 0x000e0025 ++#define HID_HP_WAVEFORMVENDORPAGE 0x000e0026 ++#define HID_HP_WAVEFORMVENDORID 0x000e0027 ++#define HID_HP_WAVEFORMCUTOFFTIME 0x000e0028 ++#define HID_HP_WAVEFORMNONE 0x000e1001 ++#define HID_HP_WAVEFORMSTOP 0x000e1002 ++#define HID_HP_WAVEFORMCLICK 0x000e1003 ++#define HID_HP_WAVEFORMBUZZCONTINUOUS 0x000e1004 ++#define HID_HP_WAVEFORMRUMBLECONTINUOUS 0x000e1005 ++#define HID_HP_WAVEFORMPRESS 0x000e1006 ++#define HID_HP_WAVEFORMRELEASE 0x000e1007 ++#define HID_HP_VENDORWAVEFORMMIN 0x000e2001 ++#define HID_HP_VENDORWAVEFORMMAX 0x000e2fff ++ + #define HID_BAT_ABSOLUTESTATEOFCHARGE 0x00850065 + + #define HID_VD_ASUS_CUSTOM_MEDIA_KEYS 0xff310076 +@@ -389,6 +412,12 @@ struct hid_item { + #define HID_BOOT_PROTOCOL 0 + + /* ++ * HID units ++ */ ++#define HID_UNIT_GRAM 0x0101 ++#define HID_UNIT_NEWTON 0xe111 ++ ++/* + * This is the global environment of the parser. This information is + * persistent for main-items. The global environment can be saved and + * restored with PUSH/POP statements. diff --git a/queue-5.15/hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch b/queue-5.15/hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch new file mode 100644 index 0000000000..9bc9e83641 --- /dev/null +++ b/queue-5.15/hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch @@ -0,0 +1,150 @@ +From stable+bounces-277176-greg=kroah.com@vger.kernel.org Fri Jul 17 20:16:04 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 14:11:59 -0400 +Subject: HID: appleir: fix UAF on pending key_up_timer in remove() +To: stable@vger.kernel.org +Cc: Manish Khadka , Jiri Kosina , Sasha Levin +Message-ID: <20260717181159.2086971-2-sashal@kernel.org> + +From: Manish Khadka + +[ Upstream commit 75fe87e19d8aff81eb2c64d15d244ab8da4de945 ] + +appleir_remove() runs hid_hw_stop() before timer_delete_sync(). +hid_hw_stop() synchronously unregisters the HID input device via +hid_disconnect() -> hidinput_disconnect() -> input_unregister_device(), +which drops the last reference and frees the underlying input_dev when +no userspace handle holds it open. + +key_up_tick() reads appleir->input_dev and calls input_report_key() / +input_sync() on it. The timer is armed from appleir_raw_event() with +a HZ/8 (~125 ms) timeout on every keydown and key-repeat report. If a +key was pressed shortly before the device is disconnected, the timer +can fire after hid_hw_stop() has freed input_dev but before the +teardown drains it. + +A simple reorder is not sufficient. Putting the timer drain first +still leaves a window where a USB URB completion (raw_event) running +during hid_hw_stop() can call mod_timer() and re-arm the timer, which +then fires after hidinput_disconnect() has freed input_dev. The same +URB-completion window also lets raw_event() reach key_up(), key_down() +and battery_flat() directly, all of which dereference +appleir->input_dev. + +Introduce a 'removing' flag on struct appleir, gated by the existing +spinlock. appleir_remove() sets the flag under the lock and then +shuts down the timer with timer_shutdown_sync(), which both drains any +in-flight callback and permanently disables further mod_timer() calls. +appleir_raw_event() and key_up_tick() bail out early if the flag is +set, so no path can arm or run the timer, or dereference +appleir->input_dev, after remove() has started tearing down. + +The keyrepeat and flatbattery branches of appleir_raw_event() +previously called into the input layer without holding the spinlock; +take it now so the flag check is well-defined. This incidentally +closes a pre-existing read-side race on appleir->current_key in the +keyrepeat branch. + +This bug is structurally a sibling of commit 4db2af929279 ("HID: +appletb-kbd: fix UAF in inactivity-timer cleanup path") and has been +present since the driver was introduced. + +Fixes: 9a4a5574ce42 ("HID: appleir: add support for Apple ir devices") +Cc: stable@vger.kernel.org +Signed-off-by: Manish Khadka +Signed-off-by: Jiri Kosina +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hid/hid-appleir.c | 45 +++++++++++++++++++++++++++++++++++---------- + 1 file changed, 35 insertions(+), 10 deletions(-) + +--- a/drivers/hid/hid-appleir.c ++++ b/drivers/hid/hid-appleir.c +@@ -109,9 +109,10 @@ struct appleir { + struct hid_device *hid; + unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; + struct timer_list key_up_timer; /* timer for key up */ +- spinlock_t lock; /* protects .current_key */ ++ spinlock_t lock; /* protects .current_key, .removing */ + int current_key; /* the currently pressed key */ + int prev_key_idx; /* key index in a 2 packets message */ ++ bool removing; /* set during teardown; gates input_dev access */ + }; + + static int get_key(int data) +@@ -172,7 +173,7 @@ static void key_up_tick(struct timer_lis + unsigned long flags; + + spin_lock_irqsave(&appleir->lock, flags); +- if (appleir->current_key) { ++ if (!appleir->removing && appleir->current_key) { + key_up(hid, appleir, appleir->current_key); + appleir->current_key = 0; + } +@@ -195,6 +196,10 @@ static int appleir_raw_event(struct hid_ + int index; + + spin_lock_irqsave(&appleir->lock, flags); ++ if (appleir->removing) { ++ spin_unlock_irqrestore(&appleir->lock, flags); ++ goto out; ++ } + /* + * If we already have a key down, take it up before marking + * this one down +@@ -229,17 +234,25 @@ static int appleir_raw_event(struct hid_ + appleir->prev_key_idx = 0; + + if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { +- key_down(hid, appleir, appleir->current_key); +- /* +- * Remote doesn't do key up, either pull them up, in the test +- * above, or here set a timer which pulls them up after 1/8 s +- */ +- mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); ++ spin_lock_irqsave(&appleir->lock, flags); ++ if (!appleir->removing) { ++ key_down(hid, appleir, appleir->current_key); ++ /* ++ * Remote doesn't do key up, either pull them up, in ++ * the test above, or here set a timer which pulls them ++ * up after 1/8 s ++ */ ++ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); ++ } ++ spin_unlock_irqrestore(&appleir->lock, flags); + goto out; + } + + if (!memcmp(data, flatbattery, sizeof(flatbattery))) { +- battery_flat(appleir); ++ spin_lock_irqsave(&appleir->lock, flags); ++ if (!appleir->removing) ++ battery_flat(appleir); ++ spin_unlock_irqrestore(&appleir->lock, flags); + /* Fall through */ + } + +@@ -318,8 +331,20 @@ fail: + static void appleir_remove(struct hid_device *hid) + { + struct appleir *appleir = hid_get_drvdata(hid); ++ unsigned long flags; ++ ++ /* ++ * Mark the driver as tearing down so that any concurrent raw_event ++ * (e.g. from a USB URB completion that hid_hw_stop() has not yet ++ * killed) and the key_up_timer softirq stop touching input_dev ++ * before hid_hw_stop() frees it via hidinput_disconnect(). ++ */ ++ spin_lock_irqsave(&appleir->lock, flags); ++ appleir->removing = true; ++ spin_unlock_irqrestore(&appleir->lock, flags); ++ ++ timer_shutdown_sync(&appleir->key_up_timer); + hid_hw_stop(hid); +- timer_delete_sync(&appleir->key_up_timer); + } + + static const struct hid_device_id appleir_devices[] = { diff --git a/queue-5.15/hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch b/queue-5.15/hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch new file mode 100644 index 0000000000..8805344096 --- /dev/null +++ b/queue-5.15/hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch @@ -0,0 +1,158 @@ +From stable+bounces-277075-greg=kroah.com@vger.kernel.org Fri Jul 17 16:34:57 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 10:34:47 -0400 +Subject: HID: multitouch: fix out-of-bounds bit access on mt_io_flags +To: stable@vger.kernel.org +Cc: Trung Nguyen , Benjamin Tissoires , Sasha Levin +Message-ID: <20260717143447.1877787-2-sashal@kernel.org> + +From: Trung Nguyen + +[ Upstream commit 8813b0612275cc61fe9e6603d0ee019247ade6be ] + +mt_io_flags is a single unsigned long, but mt_process_slot(), +mt_release_pending_palms() and mt_release_contacts() use it as a +per-slot bitmap indexed by the slot number. That slot number is only +bounded by td->maxcontacts, which is taken from the device's +ContactCountMaximum feature report and can be up to 255, not by +BITS_PER_LONG. + +As a result, a multitouch device that advertises a large contact count +makes set_bit()/clear_bit() operate past the mt_io_flags word and +corrupt the adjacent members of struct mt_device. The sticky-fingers +release timer is the easiest way to reach this. mt_release_contacts() +runs + + for (i = 0; i < mt->num_slots; i++) + clear_bit(i, &td->mt_io_flags); + +with num_slots == maxcontacts. For maxcontacts around 250 the loop +clears the bits that overlap td->applications.next, zeroing that list +head, and the list_for_each_entry() that immediately follows then +dereferences NULL. The kernel panics from timer (softirq) context. On a +KASAN build this shows up as a general protection fault in +mt_release_contacts() with a null-ptr-deref at offset 0x58, which is +offsetof(struct mt_application, num_received). + +The state is reachable from an untrusted USB or Bluetooth HID +multitouch device; no local privileges are required. + +Store the per-slot active state in a separately allocated bitmap sized +for maxcontacts, the same pattern already used for pending_palm_slots, +and keep only MT_IO_FLAGS_RUNNING in mt_io_flags. The two +"mt_io_flags & MT_IO_SLOTS_MASK" arming checks become +bitmap_empty(td->active_slots, td->maxcontacts). + +Move MT_IO_FLAGS_RUNNING back to bit 0. It was bumped to bit 32 by the +same commit to leave the low byte for the slot bits; with the slot bits +gone it fits in bit 0 again, which also keeps it within the unsigned +long on 32-bit. + +Fixes: 46f781e0d151 ("HID: multitouch: fix sticky fingers") +Cc: stable@vger.kernel.org +Signed-off-by: Trung Nguyen +Signed-off-by: Benjamin Tissoires +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hid/hid-multitouch.c | 32 ++++++++++++++++++++------------ + 1 file changed, 20 insertions(+), 12 deletions(-) + +--- a/drivers/hid/hid-multitouch.c ++++ b/drivers/hid/hid-multitouch.c +@@ -31,6 +31,7 @@ + * [1] https://gitlab.freedesktop.org/libevdev/hid-tools + */ + ++#include + #include + #include + #include +@@ -83,8 +84,7 @@ enum latency_mode { + HID_LATENCY_HIGH = 1, + }; + +-#define MT_IO_SLOTS_MASK GENMASK(7, 0) /* reserve first 8 bits for slot tracking */ +-#define MT_IO_FLAGS_RUNNING 32 ++#define MT_IO_FLAGS_RUNNING 0 + + static const bool mtrue = true; /* default for true */ + static const bool mfalse; /* default for false */ +@@ -160,10 +160,9 @@ struct mt_device { + struct mt_class mtclass; /* our mt device class */ + struct timer_list release_timer; /* to release sticky fingers */ + struct hid_device *hdev; /* hid_device we're attached to */ +- unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) +- * first 8 bits are reserved for keeping the slot +- * states, this is fine because we only support up +- * to 250 slots (MT_MAX_MAXCONTACT) ++ unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_RUNNING) */ ++ unsigned long *active_slots; /* bitmap of slots with an active ++ * contact, sized for maxcontacts + */ + __u8 inputmode_value; /* InputMode HID feature value */ + __u8 maxcontacts; +@@ -947,7 +946,7 @@ static void mt_release_pending_palms(str + + for_each_set_bit(slotnum, app->pending_palm_slots, td->maxcontacts) { + clear_bit(slotnum, app->pending_palm_slots); +- clear_bit(slotnum, &td->mt_io_flags); ++ clear_bit(slotnum, td->active_slots); + + input_mt_slot(input, slotnum); + input_mt_report_slot_inactive(input); +@@ -1153,9 +1152,9 @@ static int mt_process_slot(struct mt_dev + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + +- set_bit(slotnum, &td->mt_io_flags); ++ set_bit(slotnum, td->active_slots); + } else { +- clear_bit(slotnum, &td->mt_io_flags); ++ clear_bit(slotnum, td->active_slots); + } + + return 0; +@@ -1290,7 +1289,7 @@ static void mt_touch_report(struct hid_d + * defect. + */ + if (app->quirks & MT_QUIRK_STICKY_FINGERS) { +- if (td->mt_io_flags & MT_IO_SLOTS_MASK) ++ if (!bitmap_empty(td->active_slots, td->maxcontacts)) + mod_timer(&td->release_timer, + jiffies + msecs_to_jiffies(100)); + else +@@ -1330,6 +1329,15 @@ static int mt_touch_input_configured(str + if (td->is_buttonpad) + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + ++ if (!td->active_slots) { ++ td->active_slots = devm_kcalloc(&td->hdev->dev, ++ BITS_TO_LONGS(td->maxcontacts), ++ sizeof(long), ++ GFP_KERNEL); ++ if (!td->active_slots) ++ return -ENOMEM; ++ } ++ + app->pending_palm_slots = devm_kcalloc(&hi->input->dev, + BITS_TO_LONGS(td->maxcontacts), + sizeof(long), +@@ -1740,7 +1748,7 @@ static void mt_release_contacts(struct h + for (i = 0; i < mt->num_slots; i++) { + input_mt_slot(input_dev, i); + input_mt_report_slot_inactive(input_dev); +- clear_bit(i, &td->mt_io_flags); ++ clear_bit(i, td->active_slots); + } + input_mt_sync_frame(input_dev); + input_sync(input_dev); +@@ -1763,7 +1771,7 @@ static void mt_expired_timeout(struct ti + */ + if (test_and_set_bit_lock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags)) + return; +- if (td->mt_io_flags & MT_IO_SLOTS_MASK) ++ if (!bitmap_empty(td->active_slots, td->maxcontacts)) + mt_release_contacts(hdev); + clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); + } diff --git a/queue-5.15/iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch b/queue-5.15/iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch new file mode 100644 index 0000000000..a2b5a52cbb --- /dev/null +++ b/queue-5.15/iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch @@ -0,0 +1,109 @@ +From stable+bounces-274080-greg=kroah.com@vger.kernel.org Tue Jul 14 04:10:06 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 22:09:08 -0400 +Subject: iio: common: st_sensors: honour channel endianness in read_axis_data +To: stable@vger.kernel.org +Cc: Herman van Hazendonk , Andy Shevchenko , Jonathan Cameron , Sasha Levin +Message-ID: <20260714020908.2355876-2-sashal@kernel.org> + +From: Herman van Hazendonk + +[ Upstream commit 55052184ac9011db2ea983e54d6c21f0b1079a12 ] + +st_sensors_read_axis_data() unconditionally decoded multi-byte +results with get_unaligned_le16() / get_unaligned_le24() regardless +of the channel's declared scan_type.endianness. + +For every ST sensor that has used this helper since it was introduced +this happened to be fine because the ST IMU/accel/gyro/pressure +families publish their data registers as little-endian and the +channel specs in those drivers declare IIO_LE accordingly. + +The LSM303DLH magnetometer however publishes its X/Y/Z output as a +pair of big-endian bytes (the H register sits at the lower address, +0x03/0x05/0x07, and the L register immediately after), and its +channel specs in st_magn_core.c correctly declare IIO_BE -- but +read_axis_data() ignored that and decoded as little-endian, swapping +the high and low bytes of every magnetometer sample. The LSM303DLHC +and LSM303DLM share the same st_magn_16bit_channels (IIO_BE) and +were therefore byte-swapped by the same bug; users of those parts +will see different in_magn_*_raw values after this fix lands. + +The bug is most visible on a stationary chip: in earth's field the +true X reading is small and the high byte sits at 0x00, so swapping +the bytes pins sysfs X at exactly the low byte's pattern (e.g. 0x00F0 += 240). Y and Z still appear "to vary" because their magnitudes are +larger and the noise in the low byte produces big swings in the +swapped high byte: + + before (LSM303DLH flat, sysfs in_magn_*_raw): + X=240 (stuck), Y= 12032..23296, Z=-16128..-9728 + + after (direct i2c-dev big-endian decode, same chip same orientation): + X≈-4096, Y≈210, Z≈80 (sensible values reflecting earth's + ambient field at low gauss range) + +Fix read_axis_data() to dispatch on ch->scan_type.endianness and +call get_unaligned_be16() / get_unaligned_be24() when the channel +declares IIO_BE. Existing IIO_LE consumers (st_accel, st_gyro, +st_pressure, st_lsm6dsx and others) are unaffected because their +channel specs already declare IIO_LE and the LE path is unchanged. + +While restructuring the branches, replace the previously implicit +silent-success-with-uninitialised-*data fall-through for +byte_for_channel outside 1..3 with an explicit return -EINVAL. No +in-tree ST sensor publishes such a channel, but the new behaviour +is strictly safer than handing userspace garbage. + +Fixes: 23491b513bcd ("iio:common: Add STMicroelectronics common library") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-7 sparse smatch clang-analyzer coccinelle checkpatch +Assisted-by: Sashiko:claude-opus-4-7 +Signed-off-by: Herman van Hazendonk +Reviewed-by: Andy Shevchenko +Signed-off-by: Jonathan Cameron +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/common/st_sensors/st_sensors_core.c | 23 +++++++++++++++++------ + 1 file changed, 17 insertions(+), 6 deletions(-) + +--- a/drivers/iio/common/st_sensors/st_sensors_core.c ++++ b/drivers/iio/common/st_sensors/st_sensors_core.c +@@ -528,6 +528,7 @@ static int st_sensors_read_axis_data(str + u8 *outdata; + struct st_sensor_data *sdata = iio_priv(indio_dev); + unsigned int byte_for_channel; ++ u32 tmp; + + byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits + + ch->scan_type.shift, 8); +@@ -540,12 +541,22 @@ static int st_sensors_read_axis_data(str + if (err < 0) + goto st_sensors_free_memory; + +- if (byte_for_channel == 1) +- *data = (s8)*outdata; +- else if (byte_for_channel == 2) +- *data = (s16)get_unaligned_le16(outdata); +- else if (byte_for_channel == 3) +- *data = (s32)sign_extend32(get_unaligned_le24(outdata), 23); ++ if (byte_for_channel == 1) { ++ tmp = *outdata; ++ } else if (byte_for_channel == 2) { ++ if (ch->scan_type.endianness == IIO_BE) ++ tmp = get_unaligned_be16(outdata); ++ else ++ tmp = get_unaligned_le16(outdata); ++ } else if (byte_for_channel == 3) { ++ if (ch->scan_type.endianness == IIO_BE) ++ tmp = get_unaligned_be24(outdata); ++ else ++ tmp = get_unaligned_le24(outdata); ++ } else { ++ return -EINVAL; ++ } ++ *data = sign_extend32(tmp, BYTES_TO_BITS(byte_for_channel) - 1); + + st_sensors_free_memory: + kfree(outdata); diff --git a/queue-5.15/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch b/queue-5.15/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch new file mode 100644 index 0000000000..527e1625a1 --- /dev/null +++ b/queue-5.15/iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch @@ -0,0 +1,89 @@ +From stable+bounces-273966-greg=kroah.com@vger.kernel.org Mon Jul 13 20:44:16 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 14:44:09 -0400 +Subject: iio: hid-sensor-rotation: Fix stale or zero output when reading raw values +To: stable@vger.kernel.org +Cc: Zhang Lixu , Andy Shevchenko , Stable@vger.kernel.org, Jonathan Cameron , Sasha Levin +Message-ID: <20260713184409.1954470-2-sashal@kernel.org> + +From: Zhang Lixu + +[ Upstream commit 3ce8d099e0afc5a7da75a2007a67f67c4f5a4af1 ] + +When reading the raw quaternion attribute (in_rot_quaternion_raw), the +driver currently returns either all zeros (if the sensor was never enabled) +or stale data (if the sensor was previously enabled) because it reads from +the internal buffer without explicitly requesting a new sample from the +sensor. + +To fix this, power up the sensor, call sensor_hub_input_attr_read_values() +to issue a synchronous GET_REPORT and receive the full quaternion data +directly into a local buffer, then decode the four components. + +Fixes: fc18dddc0625 ("iio: hid-sensors: Added device rotation support") +Signed-off-by: Zhang Lixu +Reviewed-by: Andy Shevchenko +Cc: +Signed-off-by: Jonathan Cameron +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/orientation/hid-sensor-rotation.c | 40 ++++++++++++++++++++++++-- + 1 file changed, 38 insertions(+), 2 deletions(-) + +--- a/drivers/iio/orientation/hid-sensor-rotation.c ++++ b/drivers/iio/orientation/hid-sensor-rotation.c +@@ -69,6 +69,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; + +@@ -78,8 +85,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-5.15/iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch b/queue-5.15/iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch new file mode 100644 index 0000000000..4c0c618c61 --- /dev/null +++ b/queue-5.15/iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch @@ -0,0 +1,49 @@ +From stable+bounces-273947-greg=kroah.com@vger.kernel.org Mon Jul 13 20:16:41 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 14:13:17 -0400 +Subject: iio: imu: adis: add IRQF_NO_THREAD to non-FIFO trigger IRQ +To: stable@vger.kernel.org +Cc: Runyu Xiao , Jonathan Cameron , Sasha Levin +Message-ID: <20260713181317.1932155-1-sashal@kernel.org> + +From: Runyu Xiao + +[ Upstream commit 6e1b9bff1202da55c464e36bd34a2b6863d7fe30 ] + +devm_adis_probe_trigger() registers iio_trigger_generic_data_rdy_poll() +through devm_request_irq() on the non-FIFO path, but it does not add +IRQF_NO_THREAD to the IRQ flags. + +When the kernel is booted with forced IRQ threading, the parent IRQ can +otherwise be threaded by the IRQ core and the subsequent IIO trigger +child IRQ is then dispatched from irq/... thread context instead of +hardirq context. Because iio_trigger_generic_data_rdy_poll() +immediately drives iio_trigger_poll(), this violates the hardirq-only +IIO trigger helper contract and can push downstream trigger consumers +through the wrong execution context. + +Add IRQF_NO_THREAD on top of the existing adis->irq_flag value for the +non-FIFO request_irq() path, while preserving the current trigger +polarity and IRQF_NO_AUTOEN behavior. + +Fixes: fec86c6b8369 ("iio: imu: adis: Add Managed device functions") +Cc: stable@vger.kernel.org +Signed-off-by: Runyu Xiao +Signed-off-by: Jonathan Cameron +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/imu/adis_trigger.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/iio/imu/adis_trigger.c ++++ b/drivers/iio/imu/adis_trigger.c +@@ -79,7 +79,7 @@ int devm_adis_probe_trigger(struct adis + + ret = devm_request_irq(&adis->spi->dev, adis->spi->irq, + &iio_trigger_generic_data_rdy_poll, +- adis->irq_flag, ++ adis->irq_flag | IRQF_NO_THREAD, + indio_dev->name, + adis->trig); + if (ret) diff --git a/queue-5.15/iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch b/queue-5.15/iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch new file mode 100644 index 0000000000..82604ec671 --- /dev/null +++ b/queue-5.15/iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch @@ -0,0 +1,72 @@ +From stable+bounces-274066-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:53 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 21:16:27 -0400 +Subject: iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading +To: stable@vger.kernel.org +Cc: Jean-Baptiste Maneyrol , Jonathan Cameron , Sasha Levin +Message-ID: <20260714011627.2188779-8-sashal@kernel.org> + +From: Jean-Baptiste Maneyrol + +[ Upstream commit affe3f077d7a4eeb25937f5323ff059a54b4712c ] + +Timestamps are made by measuring the chip clock using the watermark +interrupts. If we read more than watermark samples as done today, we +are reducing the period between interrupts and distort the time +measurement. Fix that by reading only watermark samples in the +interrupt case. + +Fixes: 7f85e42a6c54 ("iio: imu: inv_icm42600: add buffer support in iio devices") +Cc: stable@vger.kernel.org +Signed-off-by: Jean-Baptiste Maneyrol +Signed-off-by: Jonathan Cameron +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 9 +++++---- + drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h | 1 + + 2 files changed, 6 insertions(+), 4 deletions(-) + +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +@@ -257,6 +257,7 @@ int inv_icm42600_buffer_update_watermark + + /* compute watermark value in bytes */ + wm_size = watermark * packet_size; ++ st->fifo.watermark.value = watermark; + + /* changing FIFO watermark requires to turn off watermark interrupt */ + ret = regmap_update_bits_check(st->map, INV_ICM42600_REG_INT_SOURCE0, +@@ -476,11 +477,10 @@ int inv_icm42600_buffer_fifo_read(struct + st->fifo.nb.accel = 0; + st->fifo.nb.total = 0; + +- /* compute maximum FIFO read size */ ++ /* compute maximum FIFO read size (watermark for max = 0 interrupt case) */ + if (max == 0) +- max_count = sizeof(st->fifo.data); +- else +- max_count = max * inv_icm42600_get_packet_size(st->fifo.en); ++ max = st->fifo.watermark.value; ++ max_count = max * inv_icm42600_get_packet_size(st->fifo.en); + + /* read FIFO count value */ + raw_fifo_count = (__be16 *)st->buffer; +@@ -592,6 +592,7 @@ int inv_icm42600_buffer_init(struct inv_ + + st->fifo.watermark.eff_gyro = 1; + st->fifo.watermark.eff_accel = 1; ++ st->fifo.watermark.value = 1; + + /* + * Default FIFO configuration (bits 7 to 5) +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +@@ -34,6 +34,7 @@ struct inv_icm42600_fifo { + unsigned int accel; + unsigned int eff_gyro; + unsigned int eff_accel; ++ unsigned int value; + } watermark; + size_t count; + struct { diff --git a/queue-5.15/iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch b/queue-5.15/iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch new file mode 100644 index 0000000000..41c28ae2b4 --- /dev/null +++ b/queue-5.15/iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch @@ -0,0 +1,103 @@ +From stable+bounces-274057-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:36 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 21:16:20 -0400 +Subject: iio: imu: inv_icm42600: make timestamp module chip independent +To: stable@vger.kernel.org +Cc: Jean-Baptiste Maneyrol , Andy Shevchenko , Jonathan Cameron , Sasha Levin +Message-ID: <20260714011627.2188779-1-sashal@kernel.org> + +From: Jean-Baptiste Maneyrol + +[ Upstream commit 6e9f2d8375cb24ba75e02e0272e9164d06a1522e ] + +Move icm42600 dependent function inside the core module. +Do some headers cleanup at the same time. + +Signed-off-by: Jean-Baptiste Maneyrol +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20230606162147.79667-2-inv.git-commit@tdk.com +Signed-off-by: Jonathan Cameron +Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 11 +++++++++++ + drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c | 14 +------------- + drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h | 6 ------ + 3 files changed, 12 insertions(+), 19 deletions(-) + +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +@@ -522,6 +522,17 @@ static int inv_icm42600_irq_init(struct + "inv_icm42600", st); + } + ++static int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st) ++{ ++ unsigned int val; ++ ++ /* enable timestamp register */ ++ val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN | ++ INV_ICM42600_TMST_CONFIG_TMST_EN; ++ return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG, ++ INV_ICM42600_TMST_CONFIG_MASK, val); ++} ++ + static int inv_icm42600_enable_regulator_vddio(struct inv_icm42600_state *st) + { + int ret; +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c +@@ -3,11 +3,10 @@ + * Copyright (C) 2020 Invensense, Inc. + */ + ++#include + #include +-#include + #include + +-#include "inv_icm42600.h" + #include "inv_icm42600_timestamp.h" + + /* internal chip period is 32kHz, 31250ns */ +@@ -56,17 +55,6 @@ void inv_icm42600_timestamp_init(struct + inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD); + } + +-int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st) +-{ +- unsigned int val; +- +- /* enable timestamp register */ +- val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN | +- INV_ICM42600_TMST_CONFIG_TMST_EN; +- return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG, +- INV_ICM42600_TMST_CONFIG_MASK, val); +-} +- + int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, + uint32_t period, bool fifo) + { +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h +@@ -6,10 +6,6 @@ + #ifndef INV_ICM42600_TIMESTAMP_H_ + #define INV_ICM42600_TIMESTAMP_H_ + +-#include +- +-struct inv_icm42600_state; +- + /** + * struct inv_icm42600_timestamp_interval - timestamps interval + * @lo: interval lower bound +@@ -53,8 +49,6 @@ struct inv_icm42600_timestamp { + void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, + uint32_t period); + +-int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st); +- + int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, + uint32_t period, bool fifo); + diff --git a/queue-5.15/iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch b/queue-5.15/iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch new file mode 100644 index 0000000000..c06a222eb7 --- /dev/null +++ b/queue-5.15/iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch @@ -0,0 +1,116 @@ +From stable+bounces-274064-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:49 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 21:16:26 -0400 +Subject: iio: imu: inv_icm42600: stabilized timestamp in interrupt +To: stable@vger.kernel.org +Cc: Jean-Baptiste Maneyrol , Jonathan Cameron , Sasha Levin +Message-ID: <20260714011627.2188779-7-sashal@kernel.org> + +From: Jean-Baptiste Maneyrol + +[ Upstream commit d7bd473632d07f8a54655c270c0940cc3671c548 ] + +Use IRQF_ONESHOT flag to ensure the timestamp is not updated in the +hard handler during the thread handler. And compute and use the +effective watermark value that correspond to this first timestamp. + +This way we can ensure the timestamp is always corresponding to the +value used by the timestamping mechanism. Otherwise, it is possible +that between FIFO count read and FIFO processing the timestamp is +overwritten in the hard handler. + +Fixes: ec74ae9fd37c ("iio: imu: inv_icm42600: add accurate timestamping") +Cc: stable@vger.kernel.org +Signed-off-by: Jean-Baptiste Maneyrol +Link: https://lore.kernel.org/r/20240529154717.651863-1-inv.git-commit@tdk.com +Signed-off-by: Jonathan Cameron +Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 19 +++++++++++++++++-- + drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h | 2 ++ + drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 1 + + 3 files changed, 20 insertions(+), 2 deletions(-) + +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +@@ -222,10 +222,15 @@ int inv_icm42600_buffer_update_watermark + latency_accel = period_accel * wm_accel; + + /* 0 value for watermark means that the sensor is turned off */ ++ if (wm_gyro == 0 && wm_accel == 0) ++ return 0; ++ + if (latency_gyro == 0) { + watermark = wm_accel; ++ st->fifo.watermark.eff_accel = wm_accel; + } else if (latency_accel == 0) { + watermark = wm_gyro; ++ st->fifo.watermark.eff_gyro = wm_gyro; + } else { + /* compute the smallest latency that is a multiple of both */ + if (latency_gyro <= latency_accel) +@@ -241,6 +246,13 @@ int inv_icm42600_buffer_update_watermark + watermark = latency / period; + if (watermark < 1) + watermark = 1; ++ /* update effective watermark */ ++ st->fifo.watermark.eff_gyro = latency / period_gyro; ++ if (st->fifo.watermark.eff_gyro < 1) ++ st->fifo.watermark.eff_gyro = 1; ++ st->fifo.watermark.eff_accel = latency / period_accel; ++ if (st->fifo.watermark.eff_accel < 1) ++ st->fifo.watermark.eff_accel = 1; + } + + /* compute watermark value in bytes */ +@@ -517,7 +529,7 @@ int inv_icm42600_buffer_fifo_parse(struc + /* handle gyroscope timestamp and FIFO data parsing */ + if (st->fifo.nb.gyro > 0) { + ts = iio_priv(st->indio_gyro); +- inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, ++ inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro, + st->timestamp.gyro); + ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); + if (ret) +@@ -527,7 +539,7 @@ int inv_icm42600_buffer_fifo_parse(struc + /* handle accelerometer timestamp and FIFO data parsing */ + if (st->fifo.nb.accel > 0) { + ts = iio_priv(st->indio_accel); +- inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, ++ inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel, + st->timestamp.accel); + ret = inv_icm42600_accel_parse_fifo(st->indio_accel); + if (ret) +@@ -578,6 +590,9 @@ int inv_icm42600_buffer_init(struct inv_ + unsigned int val; + int ret; + ++ st->fifo.watermark.eff_gyro = 1; ++ st->fifo.watermark.eff_accel = 1; ++ + /* + * Default FIFO configuration (bits 7 to 5) + * - use invalid value +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +@@ -32,6 +32,8 @@ struct inv_icm42600_fifo { + struct { + unsigned int gyro; + unsigned int accel; ++ unsigned int eff_gyro; ++ unsigned int eff_accel; + } watermark; + size_t count; + struct { +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +@@ -518,6 +518,7 @@ static int inv_icm42600_irq_init(struct + if (ret) + return ret; + ++ irq_type |= IRQF_ONESHOT; + return devm_request_threaded_irq(dev, irq, inv_icm42600_irq_timestamp, + inv_icm42600_irq_handler, irq_type, + "inv_icm42600", st); diff --git a/queue-5.15/iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch b/queue-5.15/iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch new file mode 100644 index 0000000000..b5ac4822df --- /dev/null +++ b/queue-5.15/iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch @@ -0,0 +1,307 @@ +From stable+bounces-274059-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:39 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 21:16:23 -0400 +Subject: iio: imu: inv_mpu6050: use the common inv_sensors timestamp module +To: stable@vger.kernel.org +Cc: Jean-Baptiste Maneyrol , Andy Shevchenko , Jonathan Cameron , Sasha Levin +Message-ID: <20260714011627.2188779-4-sashal@kernel.org> + +From: Jean-Baptiste Maneyrol + +[ Upstream commit 111e1abd00455971f6a568900f033cbddec9d4e5 ] + +Replace timestamping by the new common inv_sensors timestamp +module. The principle behind is the same but the implementation in +the new module is far better providing less jitter and a better +estimation. + +Signed-off-by: Jean-Baptiste Maneyrol +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20230606162147.79667-5-inv.git-commit@tdk.com +Signed-off-by: Jonathan Cameron +Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/imu/inv_mpu6050/Kconfig | 1 + drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 30 +++++++-- + drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 18 ++--- + drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c | 83 ++------------------------ + drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c | 6 + + 5 files changed, 45 insertions(+), 93 deletions(-) + +--- a/drivers/iio/imu/inv_mpu6050/Kconfig ++++ b/drivers/iio/imu/inv_mpu6050/Kconfig +@@ -7,6 +7,7 @@ config INV_MPU6050_IIO + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER ++ select IIO_INV_SENSORS_TIMESTAMP + + config INV_MPU6050_I2C + tristate "Invensense MPU6050 devices (I2C)" +--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c ++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +@@ -12,12 +12,15 @@ + #include + #include + #include +-#include + #include + #include + #include + #include + #include ++ ++#include ++#include ++ + #include "inv_mpu_iio.h" + #include "inv_mpu_magn.h" + +@@ -503,6 +506,7 @@ static int inv_mpu6050_init_config(struc + int result; + u8 d; + struct inv_mpu6050_state *st = iio_priv(indio_dev); ++ struct inv_sensors_timestamp_chip timestamp; + + result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr); + if (result) +@@ -526,12 +530,12 @@ static int inv_mpu6050_init_config(struc + if (result) + return result; + +- /* +- * Internal chip period is 1ms (1kHz). +- * Let's use at the beginning the theorical value before measuring +- * with interrupt timestamps. +- */ +- st->chip_period = NSEC_PER_MSEC; ++ /* clock jitter is +/- 2% */ ++ timestamp.clock_period = NSEC_PER_SEC / INV_MPU6050_INTERNAL_FREQ_HZ; ++ timestamp.jitter = 20; ++ timestamp.init_period = ++ NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); ++ inv_sensors_timestamp_init(&st->timestamp, ×tamp); + + /* magn chip init, noop if not present in the chip */ + result = inv_mpu_magn_probe(st); +@@ -918,6 +922,8 @@ inv_mpu6050_fifo_rate_store(struct devic + const char *buf, size_t count) + { + int fifo_rate; ++ u32 fifo_period; ++ bool fifo_on; + u8 d; + int result; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); +@@ -934,12 +940,21 @@ inv_mpu6050_fifo_rate_store(struct devic + d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate); + /* compute back the fifo rate to handle truncation cases */ + fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d); ++ fifo_period = NSEC_PER_SEC / fifo_rate; + + mutex_lock(&st->lock); + if (d == st->chip_config.divider) { + result = 0; + goto fifo_rate_fail_unlock; + } ++ ++ fifo_on = st->chip_config.accl_fifo_enable || ++ st->chip_config.gyro_fifo_enable || ++ st->chip_config.magn_fifo_enable; ++ result = inv_sensors_timestamp_update_odr(&st->timestamp, fifo_period, fifo_on); ++ if (result) ++ goto fifo_rate_fail_unlock; ++ + result = pm_runtime_resume_and_get(pdev); + if (result) + goto fifo_rate_fail_unlock; +@@ -1767,3 +1782,4 @@ EXPORT_SYMBOL_GPL(inv_mpu_pmops); + MODULE_AUTHOR("Invensense Corporation"); + MODULE_DESCRIPTION("Invensense device MPU6050 driver"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP); +--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h ++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +@@ -9,15 +9,17 @@ + #include + #include + #include +-#include +-#include ++#include + #include +-#include ++ ++#include ++#include ++#include + #include + #include + #include + #include +-#include ++#include + + /** + * struct inv_mpu6050_reg_map - Notable registers. +@@ -168,9 +170,7 @@ struct inv_mpu6050_hw { + * @map regmap pointer. + * @irq interrupt number. + * @irq_mask the int_pin_cfg mask to configure interrupt type. +- * @chip_period: chip internal period estimation (~1kHz). +- * @it_timestamp: timestamp from previous interrupt. +- * @data_timestamp: timestamp for next data sample. ++ * @timestamp: timestamping module + * @vdd_supply: VDD voltage regulator for the chip. + * @vddio_supply I/O voltage regulator for the chip. + * @magn_disabled: magnetometer disabled for backward compatibility reason. +@@ -194,9 +194,7 @@ struct inv_mpu6050_state { + int irq; + u8 irq_mask; + unsigned skip_samples; +- s64 chip_period; +- s64 it_timestamp; +- s64 data_timestamp; ++ struct inv_sensors_timestamp timestamp; + struct regulator *vdd_supply; + struct regulator *vddio_supply; + bool magn_disabled; +--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c ++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +@@ -13,81 +13,10 @@ + #include + #include + #include +-#include "inv_mpu_iio.h" +- +-/** +- * inv_mpu6050_update_period() - Update chip internal period estimation +- * +- * @st: driver state +- * @timestamp: the interrupt timestamp +- * @nb: number of data set in the fifo +- * +- * This function uses interrupt timestamps to estimate the chip period and +- * to choose the data timestamp to come. +- */ +-static void inv_mpu6050_update_period(struct inv_mpu6050_state *st, +- s64 timestamp, size_t nb) +-{ +- /* Period boundaries for accepting timestamp */ +- const s64 period_min = +- (NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100; +- const s64 period_max = +- (NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100; +- const s32 divider = INV_MPU6050_FREQ_DIVIDER(st); +- s64 delta, interval; +- bool use_it_timestamp = false; +- +- if (st->it_timestamp == 0) { +- /* not initialized, forced to use it_timestamp */ +- use_it_timestamp = true; +- } else if (nb == 1) { +- /* +- * Validate the use of it timestamp by checking if interrupt +- * has been delayed. +- * nb > 1 means interrupt was delayed for more than 1 sample, +- * so it's obviously not good. +- * Compute the chip period between 2 interrupts for validating. +- */ +- delta = div_s64(timestamp - st->it_timestamp, divider); +- if (delta > period_min && delta < period_max) { +- /* update chip period and use it timestamp */ +- st->chip_period = (st->chip_period + delta) / 2; +- use_it_timestamp = true; +- } +- } + +- if (use_it_timestamp) { +- /* +- * Manage case of multiple samples in the fifo (nb > 1): +- * compute timestamp corresponding to the first sample using +- * estimated chip period. +- */ +- interval = (nb - 1) * st->chip_period * divider; +- st->data_timestamp = timestamp - interval; +- } ++#include + +- /* save it timestamp */ +- st->it_timestamp = timestamp; +-} +- +-/** +- * inv_mpu6050_get_timestamp() - Return the current data timestamp +- * +- * @st: driver state +- * @return: current data timestamp +- * +- * This function returns the current data timestamp and prepares for next one. +- */ +-static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st) +-{ +- s64 ts; +- +- /* return current data timestamp and increment */ +- ts = st->data_timestamp; +- st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st); +- +- return ts; +-} ++#include "inv_mpu_iio.h" + + static int inv_reset_fifo(struct iio_dev *indio_dev) + { +@@ -121,6 +50,7 @@ irqreturn_t inv_mpu6050_read_fifo(int ir + size_t bytes_per_datum; + int result; + u16 fifo_count; ++ u32 fifo_period; + s64 timestamp; + int int_status; + size_t i, nb; +@@ -177,7 +107,10 @@ irqreturn_t inv_mpu6050_read_fifo(int ir + + /* compute and process all complete datum */ + nb = fifo_count / bytes_per_datum; +- inv_mpu6050_update_period(st, pf->timestamp, nb); ++ /* Each FIFO data contains all sensors, so same number for FIFO and sensor data */ ++ fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); ++ inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp); ++ inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0); + for (i = 0; i < nb; ++i) { + result = regmap_noinc_read(st->map, st->reg->fifo_r_w, + st->data, bytes_per_datum); +@@ -188,7 +121,7 @@ irqreturn_t inv_mpu6050_read_fifo(int ir + st->skip_samples--; + continue; + } +- timestamp = inv_mpu6050_get_timestamp(st); ++ timestamp = inv_sensors_timestamp_pop(&st->timestamp); + iio_push_to_buffers_with_timestamp(indio_dev, st->data, timestamp); + } + +--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c ++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +@@ -4,6 +4,9 @@ + */ + + #include ++ ++#include ++ + #include "inv_mpu_iio.h" + + static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev) +@@ -106,7 +109,8 @@ int inv_mpu6050_prepare_fifo(struct inv_ + int ret; + + if (enable) { +- st->it_timestamp = 0; ++ /* reset timestamping */ ++ inv_sensors_timestamp_reset(&st->timestamp); + /* reset FIFO */ + d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST; + ret = regmap_write(st->map, st->reg->user_ctrl, d); diff --git a/queue-5.15/iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch b/queue-5.15/iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch new file mode 100644 index 0000000000..ad435b052c --- /dev/null +++ b/queue-5.15/iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch @@ -0,0 +1,202 @@ +From stable+bounces-274065-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:52 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 21:16:25 -0400 +Subject: iio: invensense: fix timestamp glitches when switching frequency +To: stable@vger.kernel.org +Cc: Jean-Baptiste Maneyrol , Stable@vger.kernel.org, Jonathan Cameron , Sasha Levin +Message-ID: <20260714011627.2188779-6-sashal@kernel.org> + +From: Jean-Baptiste Maneyrol + +[ Upstream commit bf8367b00c33c64a9391c262bb2e11d274c9f2a4 ] + +When a sensor is running and there is a FIFO frequency change due to +another sensor turned on/off, there are glitches on timestamp. Fix that +by using only interrupt timestamp when there is the corresponding sensor +data in the FIFO. + +Delete FIFO period handling and simplify internal functions. + +Update integration inside inv_mpu6050 and inv_icm42600 drivers. + +Fixes: 0ecc363ccea7 ("iio: make invensense timestamp module generic") +Cc: Stable@vger.kernel.org +Signed-off-by: Jean-Baptiste Maneyrol +Link: https://lore.kernel.org/r/20240426094835.138389-1-inv.git-commit@tdk.com +Signed-off-by: Jonathan Cameron +Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/common/inv_sensors/inv_sensors_timestamp.c | 30 +++++++---------- + drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 20 ++++------- + drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c | 2 - + include/linux/iio/common/inv_sensors_timestamp.h | 3 - + 4 files changed, 23 insertions(+), 32 deletions(-) + +--- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c ++++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +@@ -70,13 +70,13 @@ int inv_sensors_timestamp_update_odr(str + } + EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP); + +-static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult) ++static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period) + { + uint32_t period_min, period_max; + + /* check that period is acceptable */ +- period_min = ts->min_period * mult; +- period_max = ts->max_period * mult; ++ period_min = ts->min_period * ts->mult; ++ period_max = ts->max_period * ts->mult; + if (period > period_min && period < period_max) + return true; + else +@@ -84,32 +84,30 @@ static bool inv_validate_period(struct i + } + + static bool inv_compute_chip_period(struct inv_sensors_timestamp *ts, +- uint32_t mult, uint32_t period) ++ uint32_t period) + { + uint32_t new_chip_period; + +- if (!inv_validate_period(ts, period, mult)) ++ if (!inv_validate_period(ts, period)) + return false; + + /* update chip internal period estimation */ +- new_chip_period = period / mult; ++ new_chip_period = period / ts->mult; + inv_update_acc(&ts->chip_period, new_chip_period); + + return true; + } + + void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- size_t sensor_nb, int64_t timestamp) ++ size_t sample_nb, int64_t timestamp) + { + struct inv_sensors_timestamp_interval *it; + int64_t delta, interval; +- const uint32_t fifo_mult = fifo_period / ts->chip.clock_period; + uint32_t period; + int32_t m; + bool valid = false; + +- if (fifo_nb == 0) ++ if (sample_nb == 0) + return; + + /* update interrupt timestamp and compute chip and sensor periods */ +@@ -119,8 +117,8 @@ void inv_sensors_timestamp_interrupt(str + delta = it->up - it->lo; + if (it->lo != 0) { + /* compute period: delta time divided by number of samples */ +- period = div_s64(delta, fifo_nb); +- valid = inv_compute_chip_period(ts, fifo_mult, period); ++ period = div_s64(delta, sample_nb); ++ valid = inv_compute_chip_period(ts, period); + /* update sensor period if chip internal period is updated */ + if (valid) + ts->period = ts->mult * ts->chip_period.val; +@@ -129,20 +127,18 @@ void inv_sensors_timestamp_interrupt(str + /* no previous data, compute theoritical value from interrupt */ + if (ts->timestamp == 0) { + /* elapsed time: sensor period * sensor samples number */ +- interval = (int64_t)ts->period * (int64_t)sensor_nb; ++ interval = (int64_t)ts->period * (int64_t)sample_nb; + ts->timestamp = it->up - interval; + return; + } + + /* if interrupt interval is valid, sync with interrupt timestamp */ + if (valid) { +- /* compute measured fifo_period */ +- fifo_period = fifo_mult * ts->chip_period.val; + /* delta time between last sample and last interrupt */ + delta = it->lo - ts->timestamp; + /* if there are multiple samples, go back to first one */ +- while (delta >= (fifo_period * 3 / 2)) +- delta -= fifo_period; ++ while (delta >= (ts->period * 3 / 2)) ++ delta -= ts->period; + /* compute maximal adjustment value */ + m = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter); + if (delta > m) +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +@@ -515,20 +515,20 @@ int inv_icm42600_buffer_fifo_parse(struc + return 0; + + /* handle gyroscope timestamp and FIFO data parsing */ +- ts = iio_priv(st->indio_gyro); +- inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, +- st->fifo.nb.gyro, st->timestamp.gyro); + if (st->fifo.nb.gyro > 0) { ++ ts = iio_priv(st->indio_gyro); ++ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, ++ st->timestamp.gyro); + ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); + if (ret) + return ret; + } + + /* handle accelerometer timestamp and FIFO data parsing */ +- ts = iio_priv(st->indio_accel); +- inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, +- st->fifo.nb.accel, st->timestamp.accel); + if (st->fifo.nb.accel > 0) { ++ ts = iio_priv(st->indio_accel); ++ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, ++ st->timestamp.accel); + ret = inv_icm42600_accel_parse_fifo(st->indio_accel); + if (ret) + return ret; +@@ -556,9 +556,7 @@ int inv_icm42600_buffer_hwfifo_flush(str + + if (st->fifo.nb.gyro > 0) { + ts = iio_priv(st->indio_gyro); +- inv_sensors_timestamp_interrupt(ts, st->fifo.period, +- st->fifo.nb.total, st->fifo.nb.gyro, +- gyro_ts); ++ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts); + ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); + if (ret) + return ret; +@@ -566,9 +564,7 @@ int inv_icm42600_buffer_hwfifo_flush(str + + if (st->fifo.nb.accel > 0) { + ts = iio_priv(st->indio_accel); +- inv_sensors_timestamp_interrupt(ts, st->fifo.period, +- st->fifo.nb.total, st->fifo.nb.accel, +- accel_ts); ++ inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts); + ret = inv_icm42600_accel_parse_fifo(st->indio_accel); + if (ret) + return ret; +--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c ++++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +@@ -109,7 +109,7 @@ irqreturn_t inv_mpu6050_read_fifo(int ir + nb = fifo_count / bytes_per_datum; + /* Each FIFO data contains all sensors, so same number for FIFO and sensor data */ + fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); +- inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp); ++ inv_sensors_timestamp_interrupt(&st->timestamp, nb, pf->timestamp); + inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0); + for (i = 0; i < nb; ++i) { + result = regmap_noinc_read(st->map, st->reg->fifo_r_w, +--- a/include/linux/iio/common/inv_sensors_timestamp.h ++++ b/include/linux/iio/common/inv_sensors_timestamp.h +@@ -71,8 +71,7 @@ int inv_sensors_timestamp_update_odr(str + uint32_t period, bool fifo); + + void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- size_t sensor_nb, int64_t timestamp); ++ size_t sample_nb, int64_t timestamp); + + static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts) + { diff --git a/queue-5.15/iio-invensense-remove-redundant-initialization-of-variable-period.patch b/queue-5.15/iio-invensense-remove-redundant-initialization-of-variable-period.patch new file mode 100644 index 0000000000..272e8eb529 --- /dev/null +++ b/queue-5.15/iio-invensense-remove-redundant-initialization-of-variable-period.patch @@ -0,0 +1,42 @@ +From stable+bounces-274060-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:39 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 21:16:24 -0400 +Subject: iio: invensense: remove redundant initialization of variable period +To: stable@vger.kernel.org +Cc: Colin Ian King , Jean-Baptiste Maneyrol , Jonathan Cameron , Sasha Levin +Message-ID: <20260714011627.2188779-5-sashal@kernel.org> + +From: Colin Ian King + +[ Upstream commit b58b13f156c00c2457035b7071eaaac105fe6836 ] + +The variable period is being initialized with a value that is never +read, it is being re-assigned a new value later on before it is read. +The initialization is redundant and can be removed. + +Cleans up clang scan build warning: +Value stored to 'period' during its initialization is never +read [deadcode.DeadStores] + +Signed-off-by: Colin Ian King +Acked-by: Jean-Baptiste Maneyrol +Link: https://lore.kernel.org/r/20240106153202.54861-1-colin.i.king@gmail.com +Signed-off-by: Jonathan Cameron +Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/common/inv_sensors/inv_sensors_timestamp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c ++++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +@@ -105,7 +105,7 @@ void inv_sensors_timestamp_interrupt(str + struct inv_sensors_timestamp_interval *it; + int64_t delta, interval; + const uint32_t fifo_mult = fifo_period / ts->chip.clock_period; +- uint32_t period = ts->period; ++ uint32_t period; + int32_t m; + bool valid = false; + diff --git a/queue-5.15/iio-make-invensense-timestamp-module-generic.patch b/queue-5.15/iio-make-invensense-timestamp-module-generic.patch new file mode 100644 index 0000000000..31d63699b4 --- /dev/null +++ b/queue-5.15/iio-make-invensense-timestamp-module-generic.patch @@ -0,0 +1,962 @@ +From stable+bounces-274062-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:45 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 21:16:22 -0400 +Subject: iio: make invensense timestamp module generic +To: stable@vger.kernel.org +Cc: Jean-Baptiste Maneyrol , Andy Shevchenko , Jonathan Cameron , Sasha Levin +Message-ID: <20260714011627.2188779-3-sashal@kernel.org> + +From: Jean-Baptiste Maneyrol + +[ Upstream commit 0ecc363ccea71eda6a2cceade120489259bcdb33 ] + +Rename common module to inv_sensors_timestamp, add configuration +at init (chip internal clock, acceptable jitter, ...) and update +inv_icm42600 driver integration. + +Signed-off-by: Jean-Baptiste Maneyrol +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20230606162147.79667-4-inv.git-commit@tdk.com +Signed-off-by: Jonathan Cameron +Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/common/inv_sensors/Makefile | 2 + drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c | 192 ---------------- + drivers/iio/common/inv_sensors/inv_sensors_timestamp.c | 191 +++++++++++++++ + drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 32 +- + drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 34 +- + drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 10 + drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c | 32 +- + include/linux/iio/common/inv_icm42600_timestamp.h | 79 ------ + include/linux/iio/common/inv_sensors_timestamp.h | 95 +++++++ + 9 files changed, 349 insertions(+), 318 deletions(-) + rename drivers/iio/common/inv_sensors/{inv_icm42600_timestamp.c => inv_sensors_timestamp.c} (61%) + delete mode 100644 include/linux/iio/common/inv_icm42600_timestamp.h + create mode 100644 include/linux/iio/common/inv_sensors_timestamp.h + +--- a/drivers/iio/common/inv_sensors/Makefile ++++ b/drivers/iio/common/inv_sensors/Makefile +@@ -3,4 +3,4 @@ + # Makefile for TDK-InvenSense sensors module. + # + +-obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_icm42600_timestamp.o ++obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_sensors_timestamp.o +--- a/drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c ++++ /dev/null +@@ -1,192 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0-or-later +-/* +- * Copyright (C) 2020 Invensense, Inc. +- */ +- +-#include +-#include +-#include +-#include +- +-#include +- +-/* internal chip period is 32kHz, 31250ns */ +-#define INV_ICM42600_TIMESTAMP_PERIOD 31250 +-/* allow a jitter of +/- 2% */ +-#define INV_ICM42600_TIMESTAMP_JITTER 2 +-/* compute min and max periods accepted */ +-#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p) \ +- (((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100) +-#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p) \ +- (((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100) +- +-/* Add a new value inside an accumulator and update the estimate value */ +-static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val) +-{ +- uint64_t sum = 0; +- size_t i; +- +- acc->values[acc->idx++] = val; +- if (acc->idx >= ARRAY_SIZE(acc->values)) +- acc->idx = 0; +- +- /* compute the mean of all stored values, use 0 as empty slot */ +- for (i = 0; i < ARRAY_SIZE(acc->values); ++i) { +- if (acc->values[i] == 0) +- break; +- sum += acc->values[i]; +- } +- +- acc->val = div_u64(sum, i); +-} +- +-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, +- uint32_t period) +-{ +- /* initial odr for sensor after reset is 1kHz */ +- const uint32_t default_period = 1000000; +- +- /* current multiplier and period values after reset */ +- ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD; +- ts->period = default_period; +- /* new set multiplier is the one from chip initialization */ +- ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD; +- +- /* use theoretical value for chip period */ +- inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD); +-} +-EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_init, IIO_INV_SENSORS_TIMESTAMP); +- +-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, +- uint32_t period, bool fifo) +-{ +- /* when FIFO is on, prevent odr change if one is already pending */ +- if (fifo && ts->new_mult != 0) +- return -EAGAIN; +- +- ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD; +- +- return 0; +-} +-EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP); +- +-static bool inv_validate_period(uint32_t period, uint32_t mult) +-{ +- const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD; +- uint32_t period_min, period_max; +- +- /* check that period is acceptable */ +- period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult; +- period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult; +- if (period > period_min && period < period_max) +- return true; +- else +- return false; +-} +- +-static bool inv_compute_chip_period(struct inv_icm42600_timestamp *ts, +- uint32_t mult, uint32_t period) +-{ +- uint32_t new_chip_period; +- +- if (!inv_validate_period(period, mult)) +- return false; +- +- /* update chip internal period estimation */ +- new_chip_period = period / mult; +- inv_update_acc(&ts->chip_period, new_chip_period); +- +- return true; +-} +- +-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- size_t sensor_nb, int64_t timestamp) +-{ +- struct inv_icm42600_timestamp_interval *it; +- int64_t delta, interval; +- const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD; +- uint32_t period = ts->period; +- int32_t m; +- bool valid = false; +- +- if (fifo_nb == 0) +- return; +- +- /* update interrupt timestamp and compute chip and sensor periods */ +- it = &ts->it; +- it->lo = it->up; +- it->up = timestamp; +- delta = it->up - it->lo; +- if (it->lo != 0) { +- /* compute period: delta time divided by number of samples */ +- period = div_s64(delta, fifo_nb); +- valid = inv_compute_chip_period(ts, fifo_mult, period); +- /* update sensor period if chip internal period is updated */ +- if (valid) +- ts->period = ts->mult * ts->chip_period.val; +- } +- +- /* no previous data, compute theoritical value from interrupt */ +- if (ts->timestamp == 0) { +- /* elapsed time: sensor period * sensor samples number */ +- interval = (int64_t)ts->period * (int64_t)sensor_nb; +- ts->timestamp = it->up - interval; +- return; +- } +- +- /* if interrupt interval is valid, sync with interrupt timestamp */ +- if (valid) { +- /* compute measured fifo_period */ +- fifo_period = fifo_mult * ts->chip_period.val; +- /* delta time between last sample and last interrupt */ +- delta = it->lo - ts->timestamp; +- /* if there are multiple samples, go back to first one */ +- while (delta >= (fifo_period * 3 / 2)) +- delta -= fifo_period; +- /* compute maximal adjustment value */ +- m = INV_ICM42600_TIMESTAMP_MAX_PERIOD(ts->period) - ts->period; +- if (delta > m) +- delta = m; +- else if (delta < -m) +- delta = -m; +- ts->timestamp += delta; +- } +-} +-EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP); +- +-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- unsigned int fifo_no) +-{ +- int64_t interval; +- uint32_t fifo_mult; +- +- if (ts->new_mult == 0) +- return; +- +- /* update to new multiplier and update period */ +- ts->mult = ts->new_mult; +- ts->new_mult = 0; +- ts->period = ts->mult * ts->chip_period.val; +- +- /* +- * After ODR change the time interval with the previous sample is +- * undertermined (depends when the change occures). So we compute the +- * timestamp from the current interrupt using the new FIFO period, the +- * total number of samples and the current sample numero. +- */ +- if (ts->timestamp != 0) { +- /* compute measured fifo period */ +- fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD; +- fifo_period = fifo_mult * ts->chip_period.val; +- /* computes time interval between interrupt and this sample */ +- interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period; +- ts->timestamp = ts->it.up - interval; +- } +-} +-EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP); +- +-MODULE_AUTHOR("InvenSense, Inc."); +-MODULE_DESCRIPTION("InvenSense sensors timestamp module"); +-MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +@@ -0,0 +1,191 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (C) 2020 Invensense, Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* compute jitter, min and max following jitter in per mille */ ++#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter) \ ++ (div_s64((_val) * (_jitter), 1000)) ++#define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter) \ ++ (((_val) * (1000 - (_jitter))) / 1000) ++#define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \ ++ (((_val) * (1000 + (_jitter))) / 1000) ++ ++/* Add a new value inside an accumulator and update the estimate value */ ++static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val) ++{ ++ uint64_t sum = 0; ++ size_t i; ++ ++ acc->values[acc->idx++] = val; ++ if (acc->idx >= ARRAY_SIZE(acc->values)) ++ acc->idx = 0; ++ ++ /* compute the mean of all stored values, use 0 as empty slot */ ++ for (i = 0; i < ARRAY_SIZE(acc->values); ++i) { ++ if (acc->values[i] == 0) ++ break; ++ sum += acc->values[i]; ++ } ++ ++ acc->val = div_u64(sum, i); ++} ++ ++void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts, ++ const struct inv_sensors_timestamp_chip *chip) ++{ ++ memset(ts, 0, sizeof(*ts)); ++ ++ /* save chip parameters and compute min and max clock period */ ++ ts->chip = *chip; ++ ts->min_period = INV_SENSORS_TIMESTAMP_MIN(chip->clock_period, chip->jitter); ++ ts->max_period = INV_SENSORS_TIMESTAMP_MAX(chip->clock_period, chip->jitter); ++ ++ /* current multiplier and period values after reset */ ++ ts->mult = chip->init_period / chip->clock_period; ++ ts->period = chip->init_period; ++ ++ /* use theoretical value for chip period */ ++ inv_update_acc(&ts->chip_period, chip->clock_period); ++} ++EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, IIO_INV_SENSORS_TIMESTAMP); ++ ++int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, ++ uint32_t period, bool fifo) ++{ ++ /* when FIFO is on, prevent odr change if one is already pending */ ++ if (fifo && ts->new_mult != 0) ++ return -EAGAIN; ++ ++ ts->new_mult = period / ts->chip.clock_period; ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP); ++ ++static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult) ++{ ++ uint32_t period_min, period_max; ++ ++ /* check that period is acceptable */ ++ period_min = ts->min_period * mult; ++ period_max = ts->max_period * mult; ++ if (period > period_min && period < period_max) ++ return true; ++ else ++ return false; ++} ++ ++static bool inv_compute_chip_period(struct inv_sensors_timestamp *ts, ++ uint32_t mult, uint32_t period) ++{ ++ uint32_t new_chip_period; ++ ++ if (!inv_validate_period(ts, period, mult)) ++ return false; ++ ++ /* update chip internal period estimation */ ++ new_chip_period = period / mult; ++ inv_update_acc(&ts->chip_period, new_chip_period); ++ ++ return true; ++} ++ ++void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, ++ uint32_t fifo_period, size_t fifo_nb, ++ size_t sensor_nb, int64_t timestamp) ++{ ++ struct inv_sensors_timestamp_interval *it; ++ int64_t delta, interval; ++ const uint32_t fifo_mult = fifo_period / ts->chip.clock_period; ++ uint32_t period = ts->period; ++ int32_t m; ++ bool valid = false; ++ ++ if (fifo_nb == 0) ++ return; ++ ++ /* update interrupt timestamp and compute chip and sensor periods */ ++ it = &ts->it; ++ it->lo = it->up; ++ it->up = timestamp; ++ delta = it->up - it->lo; ++ if (it->lo != 0) { ++ /* compute period: delta time divided by number of samples */ ++ period = div_s64(delta, fifo_nb); ++ valid = inv_compute_chip_period(ts, fifo_mult, period); ++ /* update sensor period if chip internal period is updated */ ++ if (valid) ++ ts->period = ts->mult * ts->chip_period.val; ++ } ++ ++ /* no previous data, compute theoritical value from interrupt */ ++ if (ts->timestamp == 0) { ++ /* elapsed time: sensor period * sensor samples number */ ++ interval = (int64_t)ts->period * (int64_t)sensor_nb; ++ ts->timestamp = it->up - interval; ++ return; ++ } ++ ++ /* if interrupt interval is valid, sync with interrupt timestamp */ ++ if (valid) { ++ /* compute measured fifo_period */ ++ fifo_period = fifo_mult * ts->chip_period.val; ++ /* delta time between last sample and last interrupt */ ++ delta = it->lo - ts->timestamp; ++ /* if there are multiple samples, go back to first one */ ++ while (delta >= (fifo_period * 3 / 2)) ++ delta -= fifo_period; ++ /* compute maximal adjustment value */ ++ m = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter); ++ if (delta > m) ++ delta = m; ++ else if (delta < -m) ++ delta = -m; ++ ts->timestamp += delta; ++ } ++} ++EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP); ++ ++void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, ++ uint32_t fifo_period, size_t fifo_nb, ++ unsigned int fifo_no) ++{ ++ int64_t interval; ++ uint32_t fifo_mult; ++ ++ if (ts->new_mult == 0) ++ return; ++ ++ /* update to new multiplier and update period */ ++ ts->mult = ts->new_mult; ++ ts->new_mult = 0; ++ ts->period = ts->mult * ts->chip_period.val; ++ ++ /* ++ * After ODR change the time interval with the previous sample is ++ * undertermined (depends when the change occures). So we compute the ++ * timestamp from the current interrupt using the new FIFO period, the ++ * total number of samples and the current sample numero. ++ */ ++ if (ts->timestamp != 0) { ++ /* compute measured fifo period */ ++ fifo_mult = fifo_period / ts->chip.clock_period; ++ fifo_period = fifo_mult * ts->chip_period.val; ++ /* computes time interval between interrupt and this sample */ ++ interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period; ++ ts->timestamp = ts->it.up - interval; ++ } ++} ++EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP); ++ ++MODULE_AUTHOR("InvenSense, Inc."); ++MODULE_DESCRIPTION("InvenSense sensors timestamp module"); ++MODULE_LICENSE("GPL"); +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +@@ -12,7 +12,7 @@ + #include + + #include +-#include ++#include + #include + #include + +@@ -99,7 +99,7 @@ static int inv_icm42600_accel_update_sca + const unsigned long *scan_mask) + { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); +- struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); ++ struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int fifo_en = 0; + unsigned int sleep_temp = 0; +@@ -127,7 +127,7 @@ static int inv_icm42600_accel_update_sca + } + + /* update data FIFO write */ +- inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0); ++ inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); + ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); + + out_unlock: +@@ -308,7 +308,7 @@ static int inv_icm42600_accel_write_odr( + int val, int val2) + { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); +- struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); ++ struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; +@@ -329,8 +329,8 @@ static int inv_icm42600_accel_write_odr( + pm_runtime_get_sync(dev); + mutex_lock(&st->lock); + +- ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr), +- iio_buffer_enabled(indio_dev)); ++ ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr), ++ iio_buffer_enabled(indio_dev)); + if (ret) + goto out_unlock; + +@@ -706,7 +706,8 @@ struct iio_dev *inv_icm42600_accel_init( + { + struct device *dev = regmap_get_device(st->map); + const char *name; +- struct inv_icm42600_timestamp *ts; ++ struct inv_sensors_timestamp_chip ts_chip; ++ struct inv_sensors_timestamp *ts; + struct iio_dev *indio_dev; + int ret; + +@@ -718,8 +719,15 @@ struct iio_dev *inv_icm42600_accel_init( + if (!indio_dev) + return ERR_PTR(-ENOMEM); + ++ /* ++ * clock period is 32kHz (31250ns) ++ * jitter is +/- 2% (20 per mille) ++ */ ++ ts_chip.clock_period = 31250; ++ ts_chip.jitter = 20; ++ ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); + ts = iio_priv(indio_dev); +- inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr)); ++ inv_sensors_timestamp_init(ts, &ts_chip); + + iio_device_set_drvdata(indio_dev, st); + indio_dev->name = name; +@@ -745,7 +753,7 @@ struct iio_dev *inv_icm42600_accel_init( + int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) + { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); +- struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); ++ struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + ssize_t i, size; + unsigned int no; + const void *accel, *gyro, *timestamp; +@@ -769,13 +777,13 @@ int inv_icm42600_accel_parse_fifo(struct + + /* update odr */ + if (odr & INV_ICM42600_SENSOR_ACCEL) +- inv_icm42600_timestamp_apply_odr(ts, st->fifo.period, +- st->fifo.nb.total, no); ++ inv_sensors_timestamp_apply_odr(ts, st->fifo.period, ++ st->fifo.nb.total, no); + + memcpy(&buffer.accel, accel, sizeof(buffer.accel)); + /* convert 8 bits FIFO temperature in high resolution format */ + buffer.temp = temp ? (*temp * 64) : 0; +- ts_val = inv_icm42600_timestamp_pop(ts); ++ ts_val = inv_sensors_timestamp_pop(ts); + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val); + } + +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +@@ -11,7 +11,7 @@ + #include + + #include +-#include ++#include + #include + + #include "inv_icm42600.h" +@@ -276,12 +276,12 @@ static int inv_icm42600_buffer_preenable + { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct device *dev = regmap_get_device(st->map); +- struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); ++ struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + + pm_runtime_get_sync(dev); + + mutex_lock(&st->lock); +- inv_icm42600_timestamp_reset(ts); ++ inv_sensors_timestamp_reset(ts); + mutex_unlock(&st->lock); + + return 0; +@@ -378,7 +378,7 @@ out_unlock: + static int inv_icm42600_buffer_postdisable(struct iio_dev *indio_dev) + { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); +- struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); ++ struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int sensor; + unsigned int *watermark; +@@ -400,7 +400,7 @@ static int inv_icm42600_buffer_postdisab + + mutex_lock(&st->lock); + +- inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0); ++ inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); + + ret = inv_icm42600_buffer_set_fifo_en(st, st->fifo.en & ~sensor); + if (ret) +@@ -508,7 +508,7 @@ int inv_icm42600_buffer_fifo_read(struct + + int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st) + { +- struct inv_icm42600_timestamp *ts; ++ struct inv_sensors_timestamp *ts; + int ret; + + if (st->fifo.nb.total == 0) +@@ -516,8 +516,8 @@ int inv_icm42600_buffer_fifo_parse(struc + + /* handle gyroscope timestamp and FIFO data parsing */ + ts = iio_priv(st->indio_gyro); +- inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, +- st->fifo.nb.gyro, st->timestamp.gyro); ++ inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, ++ st->fifo.nb.gyro, st->timestamp.gyro); + if (st->fifo.nb.gyro > 0) { + ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); + if (ret) +@@ -526,8 +526,8 @@ int inv_icm42600_buffer_fifo_parse(struc + + /* handle accelerometer timestamp and FIFO data parsing */ + ts = iio_priv(st->indio_accel); +- inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, +- st->fifo.nb.accel, st->timestamp.accel); ++ inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, ++ st->fifo.nb.accel, st->timestamp.accel); + if (st->fifo.nb.accel > 0) { + ret = inv_icm42600_accel_parse_fifo(st->indio_accel); + if (ret) +@@ -540,7 +540,7 @@ int inv_icm42600_buffer_fifo_parse(struc + int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st, + unsigned int count) + { +- struct inv_icm42600_timestamp *ts; ++ struct inv_sensors_timestamp *ts; + int64_t gyro_ts, accel_ts; + int ret; + +@@ -556,9 +556,9 @@ int inv_icm42600_buffer_hwfifo_flush(str + + if (st->fifo.nb.gyro > 0) { + ts = iio_priv(st->indio_gyro); +- inv_icm42600_timestamp_interrupt(ts, st->fifo.period, +- st->fifo.nb.total, st->fifo.nb.gyro, +- gyro_ts); ++ inv_sensors_timestamp_interrupt(ts, st->fifo.period, ++ st->fifo.nb.total, st->fifo.nb.gyro, ++ gyro_ts); + ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); + if (ret) + return ret; +@@ -566,9 +566,9 @@ int inv_icm42600_buffer_hwfifo_flush(str + + if (st->fifo.nb.accel > 0) { + ts = iio_priv(st->indio_accel); +- inv_icm42600_timestamp_interrupt(ts, st->fifo.period, +- st->fifo.nb.total, st->fifo.nb.accel, +- accel_ts); ++ inv_sensors_timestamp_interrupt(ts, st->fifo.period, ++ st->fifo.nb.total, st->fifo.nb.accel, ++ accel_ts); + ret = inv_icm42600_accel_parse_fifo(st->indio_accel); + if (ret) + return ret; +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +@@ -16,7 +16,7 @@ + #include + #include + +-#include ++#include + #include + + #include "inv_icm42600.h" +@@ -720,8 +720,8 @@ out_unlock: + static int __maybe_unused inv_icm42600_resume(struct device *dev) + { + struct inv_icm42600_state *st = dev_get_drvdata(dev); +- struct inv_icm42600_timestamp *gyro_ts = iio_priv(st->indio_gyro); +- struct inv_icm42600_timestamp *accel_ts = iio_priv(st->indio_accel); ++ struct inv_sensors_timestamp *gyro_ts = iio_priv(st->indio_gyro); ++ struct inv_sensors_timestamp *accel_ts = iio_priv(st->indio_accel); + int ret = 0; + + mutex_lock(&st->lock); +@@ -742,8 +742,8 @@ static int __maybe_unused inv_icm42600_r + + /* restore FIFO data streaming */ + if (st->fifo.on) { +- inv_icm42600_timestamp_reset(gyro_ts); +- inv_icm42600_timestamp_reset(accel_ts); ++ inv_sensors_timestamp_reset(gyro_ts); ++ inv_sensors_timestamp_reset(accel_ts); + ret = regmap_write(st->map, INV_ICM42600_REG_FIFO_CONFIG, + INV_ICM42600_FIFO_CONFIG_STREAM); + } +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +@@ -12,7 +12,7 @@ + #include + + #include +-#include ++#include + #include + #include + +@@ -99,7 +99,7 @@ static int inv_icm42600_gyro_update_scan + const unsigned long *scan_mask) + { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); +- struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); ++ struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; + unsigned int fifo_en = 0; + unsigned int sleep_gyro = 0; +@@ -127,7 +127,7 @@ static int inv_icm42600_gyro_update_scan + } + + /* update data FIFO write */ +- inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0); ++ inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); + ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); + + out_unlock: +@@ -320,7 +320,7 @@ static int inv_icm42600_gyro_write_odr(s + int val, int val2) + { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); +- struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); ++ struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct device *dev = regmap_get_device(st->map); + unsigned int idx; + struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; +@@ -341,8 +341,8 @@ static int inv_icm42600_gyro_write_odr(s + pm_runtime_get_sync(dev); + mutex_lock(&st->lock); + +- ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr), +- iio_buffer_enabled(indio_dev)); ++ ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr), ++ iio_buffer_enabled(indio_dev)); + if (ret) + goto out_unlock; + +@@ -717,7 +717,8 @@ struct iio_dev *inv_icm42600_gyro_init(s + { + struct device *dev = regmap_get_device(st->map); + const char *name; +- struct inv_icm42600_timestamp *ts; ++ struct inv_sensors_timestamp_chip ts_chip; ++ struct inv_sensors_timestamp *ts; + struct iio_dev *indio_dev; + int ret; + +@@ -729,8 +730,15 @@ struct iio_dev *inv_icm42600_gyro_init(s + if (!indio_dev) + return ERR_PTR(-ENOMEM); + ++ /* ++ * clock period is 32kHz (31250ns) ++ * jitter is +/- 2% (20 per mille) ++ */ ++ ts_chip.clock_period = 31250; ++ ts_chip.jitter = 20; ++ ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); + ts = iio_priv(indio_dev); +- inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr)); ++ inv_sensors_timestamp_init(ts, &ts_chip); + + iio_device_set_drvdata(indio_dev, st); + indio_dev->name = name; +@@ -757,7 +765,7 @@ struct iio_dev *inv_icm42600_gyro_init(s + int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev) + { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); +- struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); ++ struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + ssize_t i, size; + unsigned int no; + const void *accel, *gyro, *timestamp; +@@ -781,13 +789,13 @@ int inv_icm42600_gyro_parse_fifo(struct + + /* update odr */ + if (odr & INV_ICM42600_SENSOR_GYRO) +- inv_icm42600_timestamp_apply_odr(ts, st->fifo.period, +- st->fifo.nb.total, no); ++ inv_sensors_timestamp_apply_odr(ts, st->fifo.period, ++ st->fifo.nb.total, no); + + memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro)); + /* convert 8 bits FIFO temperature in high resolution format */ + buffer.temp = temp ? (*temp * 64) : 0; +- ts_val = inv_icm42600_timestamp_pop(ts); ++ ts_val = inv_sensors_timestamp_pop(ts); + iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val); + } + +--- a/include/linux/iio/common/inv_icm42600_timestamp.h ++++ /dev/null +@@ -1,79 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (C) 2020 Invensense, Inc. +- */ +- +-#ifndef INV_ICM42600_TIMESTAMP_H_ +-#define INV_ICM42600_TIMESTAMP_H_ +- +-/** +- * struct inv_icm42600_timestamp_interval - timestamps interval +- * @lo: interval lower bound +- * @up: interval upper bound +- */ +-struct inv_icm42600_timestamp_interval { +- int64_t lo; +- int64_t up; +-}; +- +-/** +- * struct inv_icm42600_timestamp_acc - accumulator for computing an estimation +- * @val: current estimation of the value, the mean of all values +- * @idx: current index of the next free place in values table +- * @values: table of all measured values, use for computing the mean +- */ +-struct inv_icm42600_timestamp_acc { +- uint32_t val; +- size_t idx; +- uint32_t values[32]; +-}; +- +-/** +- * struct inv_icm42600_timestamp - timestamp management states +- * @it: interrupts interval timestamps +- * @timestamp: store last timestamp for computing next data timestamp +- * @mult: current internal period multiplier +- * @new_mult: new set internal period multiplier (not yet effective) +- * @period: measured current period of the sensor +- * @chip_period: accumulator for computing internal chip period +- */ +-struct inv_icm42600_timestamp { +- struct inv_icm42600_timestamp_interval it; +- int64_t timestamp; +- uint32_t mult; +- uint32_t new_mult; +- uint32_t period; +- struct inv_icm42600_timestamp_acc chip_period; +-}; +- +-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, +- uint32_t period); +- +-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, +- uint32_t period, bool fifo); +- +-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- size_t sensor_nb, int64_t timestamp); +- +-static inline int64_t +-inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts) +-{ +- ts->timestamp += ts->period; +- return ts->timestamp; +-} +- +-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- unsigned int fifo_no); +- +-static inline void +-inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts) +-{ +- const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL}; +- +- ts->it = interval_init; +- ts->timestamp = 0; +-} +- +-#endif +--- /dev/null ++++ b/include/linux/iio/common/inv_sensors_timestamp.h +@@ -0,0 +1,95 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (C) 2020 Invensense, Inc. ++ */ ++ ++#ifndef INV_SENSORS_TIMESTAMP_H_ ++#define INV_SENSORS_TIMESTAMP_H_ ++ ++/** ++ * struct inv_sensors_timestamp_chip - chip internal properties ++ * @clock_period: internal clock period in ns ++ * @jitter: acceptable jitter in per-mille ++ * @init_period: chip initial period at reset in ns ++ */ ++struct inv_sensors_timestamp_chip { ++ uint32_t clock_period; ++ uint32_t jitter; ++ uint32_t init_period; ++}; ++ ++/** ++ * struct inv_sensors_timestamp_interval - timestamps interval ++ * @lo: interval lower bound ++ * @up: interval upper bound ++ */ ++struct inv_sensors_timestamp_interval { ++ int64_t lo; ++ int64_t up; ++}; ++ ++/** ++ * struct inv_sensors_timestamp_acc - accumulator for computing an estimation ++ * @val: current estimation of the value, the mean of all values ++ * @idx: current index of the next free place in values table ++ * @values: table of all measured values, use for computing the mean ++ */ ++struct inv_sensors_timestamp_acc { ++ uint32_t val; ++ size_t idx; ++ uint32_t values[32]; ++}; ++ ++/** ++ * struct inv_sensors_timestamp - timestamp management states ++ * @chip: chip internal characteristics ++ * @min_period: minimal acceptable clock period ++ * @max_period: maximal acceptable clock period ++ * @it: interrupts interval timestamps ++ * @timestamp: store last timestamp for computing next data timestamp ++ * @mult: current internal period multiplier ++ * @new_mult: new set internal period multiplier (not yet effective) ++ * @period: measured current period of the sensor ++ * @chip_period: accumulator for computing internal chip period ++ */ ++struct inv_sensors_timestamp { ++ struct inv_sensors_timestamp_chip chip; ++ uint32_t min_period; ++ uint32_t max_period; ++ struct inv_sensors_timestamp_interval it; ++ int64_t timestamp; ++ uint32_t mult; ++ uint32_t new_mult; ++ uint32_t period; ++ struct inv_sensors_timestamp_acc chip_period; ++}; ++ ++void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts, ++ const struct inv_sensors_timestamp_chip *chip); ++ ++int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, ++ uint32_t period, bool fifo); ++ ++void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, ++ uint32_t fifo_period, size_t fifo_nb, ++ size_t sensor_nb, int64_t timestamp); ++ ++static inline int64_t inv_sensors_timestamp_pop(struct inv_sensors_timestamp *ts) ++{ ++ ts->timestamp += ts->period; ++ return ts->timestamp; ++} ++ ++void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, ++ uint32_t fifo_period, size_t fifo_nb, ++ unsigned int fifo_no); ++ ++static inline void inv_sensors_timestamp_reset(struct inv_sensors_timestamp *ts) ++{ ++ const struct inv_sensors_timestamp_interval interval_init = {0LL, 0LL}; ++ ++ ts->it = interval_init; ++ ts->timestamp = 0; ++} ++ ++#endif diff --git a/queue-5.15/iio-move-inv_icm42600-timestamp-module-in-common.patch b/queue-5.15/iio-move-inv_icm42600-timestamp-module-in-common.patch new file mode 100644 index 0000000000..3bad550c8c --- /dev/null +++ b/queue-5.15/iio-move-inv_icm42600-timestamp-module-in-common.patch @@ -0,0 +1,741 @@ +From stable+bounces-274058-greg=kroah.com@vger.kernel.org Tue Jul 14 03:16:34 2026 +From: Sasha Levin +Date: Mon, 13 Jul 2026 21:16:21 -0400 +Subject: iio: move inv_icm42600 timestamp module in common +To: stable@vger.kernel.org +Cc: Jean-Baptiste Maneyrol , Andy Shevchenko , Jonathan Cameron , Sasha Levin +Message-ID: <20260714011627.2188779-2-sashal@kernel.org> + +From: Jean-Baptiste Maneyrol + +[ Upstream commit d99ff463ecf651437e9e4abe68f331dfb6b5bd9d ] + +Create new inv_sensors common modules and move inv_icm42600 +timestamp module inside. This module will be used by IMUs and +also in the future by other chips. + +Modify inv_icm42600 driver to use timestamp module and do some +headers cleanup. + +Signed-off-by: Jean-Baptiste Maneyrol +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20230606162147.79667-3-inv.git-commit@tdk.com +Signed-off-by: Jonathan Cameron +Stable-dep-of: affe3f077d7a ("iio: imu: inv_icm42600: fix timestamping by limiting FIFO reading") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/iio/common/Kconfig | 1 + + drivers/iio/common/Makefile | 1 + + drivers/iio/common/inv_sensors/Kconfig | 7 +++++++ + drivers/iio/common/inv_sensors/Makefile | 6 ++++++ + .../inv_sensors}/inv_icm42600_timestamp.c | 11 ++++++++++- + drivers/iio/imu/inv_icm42600/Kconfig | 1 + + drivers/iio/imu/inv_icm42600/Makefile | 1 - + drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 5 +++-- + drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 5 +++-- + drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 4 +++- + drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c | 5 +++-- + .../linux/iio/common}/inv_icm42600_timestamp.h | 0 + drivers/iio/common/Kconfig | 1 + drivers/iio/common/Makefile | 1 + drivers/iio/common/inv_sensors/Kconfig | 7 + drivers/iio/common/inv_sensors/Makefile | 6 + drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c | 192 ++++++++++++++++ + drivers/iio/imu/inv_icm42600/Kconfig | 1 + drivers/iio/imu/inv_icm42600/Makefile | 1 + drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c | 5 + drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c | 5 + drivers/iio/imu/inv_icm42600/inv_icm42600_core.c | 4 + drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c | 5 + drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c | 183 --------------- + drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h | 79 ------ + include/linux/iio/common/inv_icm42600_timestamp.h | 79 ++++++ + 14 files changed, 299 insertions(+), 270 deletions(-) + create mode 100644 drivers/iio/common/inv_sensors/Kconfig + create mode 100644 drivers/iio/common/inv_sensors/Makefile + rename drivers/iio/{imu/inv_icm42600 => common/inv_sensors}/inv_icm42600_timestamp.c (91%) + rename {drivers/iio/imu/inv_icm42600 => include/linux/iio/common}/inv_icm42600_timestamp.h (100%) + +--- a/drivers/iio/common/Kconfig ++++ b/drivers/iio/common/Kconfig +@@ -5,6 +5,7 @@ + + source "drivers/iio/common/cros_ec_sensors/Kconfig" + source "drivers/iio/common/hid-sensors/Kconfig" ++source "drivers/iio/common/inv_sensors/Kconfig" + source "drivers/iio/common/ms_sensors/Kconfig" + source "drivers/iio/common/scmi_sensors/Kconfig" + source "drivers/iio/common/ssp_sensors/Kconfig" +--- a/drivers/iio/common/Makefile ++++ b/drivers/iio/common/Makefile +@@ -10,6 +10,7 @@ + # When adding new entries keep the list in alphabetical order + obj-y += cros_ec_sensors/ + obj-y += hid-sensors/ ++obj-y += inv_sensors/ + obj-y += ms_sensors/ + obj-y += scmi_sensors/ + obj-y += ssp_sensors/ +--- /dev/null ++++ b/drivers/iio/common/inv_sensors/Kconfig +@@ -0,0 +1,7 @@ ++# SPDX-License-Identifier: GPL-2.0-only ++# ++# TDK-InvenSense sensors common library ++# ++ ++config IIO_INV_SENSORS_TIMESTAMP ++ tristate +--- /dev/null ++++ b/drivers/iio/common/inv_sensors/Makefile +@@ -0,0 +1,6 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Makefile for TDK-InvenSense sensors module. ++# ++ ++obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_icm42600_timestamp.o +--- /dev/null ++++ b/drivers/iio/common/inv_sensors/inv_icm42600_timestamp.c +@@ -0,0 +1,192 @@ ++// SPDX-License-Identifier: GPL-2.0-or-later ++/* ++ * Copyright (C) 2020 Invensense, Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* internal chip period is 32kHz, 31250ns */ ++#define INV_ICM42600_TIMESTAMP_PERIOD 31250 ++/* allow a jitter of +/- 2% */ ++#define INV_ICM42600_TIMESTAMP_JITTER 2 ++/* compute min and max periods accepted */ ++#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p) \ ++ (((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100) ++#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p) \ ++ (((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100) ++ ++/* Add a new value inside an accumulator and update the estimate value */ ++static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val) ++{ ++ uint64_t sum = 0; ++ size_t i; ++ ++ acc->values[acc->idx++] = val; ++ if (acc->idx >= ARRAY_SIZE(acc->values)) ++ acc->idx = 0; ++ ++ /* compute the mean of all stored values, use 0 as empty slot */ ++ for (i = 0; i < ARRAY_SIZE(acc->values); ++i) { ++ if (acc->values[i] == 0) ++ break; ++ sum += acc->values[i]; ++ } ++ ++ acc->val = div_u64(sum, i); ++} ++ ++void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, ++ uint32_t period) ++{ ++ /* initial odr for sensor after reset is 1kHz */ ++ const uint32_t default_period = 1000000; ++ ++ /* current multiplier and period values after reset */ ++ ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD; ++ ts->period = default_period; ++ /* new set multiplier is the one from chip initialization */ ++ ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD; ++ ++ /* use theoretical value for chip period */ ++ inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD); ++} ++EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_init, IIO_INV_SENSORS_TIMESTAMP); ++ ++int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, ++ uint32_t period, bool fifo) ++{ ++ /* when FIFO is on, prevent odr change if one is already pending */ ++ if (fifo && ts->new_mult != 0) ++ return -EAGAIN; ++ ++ ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD; ++ ++ return 0; ++} ++EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP); ++ ++static bool inv_validate_period(uint32_t period, uint32_t mult) ++{ ++ const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD; ++ uint32_t period_min, period_max; ++ ++ /* check that period is acceptable */ ++ period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult; ++ period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult; ++ if (period > period_min && period < period_max) ++ return true; ++ else ++ return false; ++} ++ ++static bool inv_compute_chip_period(struct inv_icm42600_timestamp *ts, ++ uint32_t mult, uint32_t period) ++{ ++ uint32_t new_chip_period; ++ ++ if (!inv_validate_period(period, mult)) ++ return false; ++ ++ /* update chip internal period estimation */ ++ new_chip_period = period / mult; ++ inv_update_acc(&ts->chip_period, new_chip_period); ++ ++ return true; ++} ++ ++void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, ++ uint32_t fifo_period, size_t fifo_nb, ++ size_t sensor_nb, int64_t timestamp) ++{ ++ struct inv_icm42600_timestamp_interval *it; ++ int64_t delta, interval; ++ const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD; ++ uint32_t period = ts->period; ++ int32_t m; ++ bool valid = false; ++ ++ if (fifo_nb == 0) ++ return; ++ ++ /* update interrupt timestamp and compute chip and sensor periods */ ++ it = &ts->it; ++ it->lo = it->up; ++ it->up = timestamp; ++ delta = it->up - it->lo; ++ if (it->lo != 0) { ++ /* compute period: delta time divided by number of samples */ ++ period = div_s64(delta, fifo_nb); ++ valid = inv_compute_chip_period(ts, fifo_mult, period); ++ /* update sensor period if chip internal period is updated */ ++ if (valid) ++ ts->period = ts->mult * ts->chip_period.val; ++ } ++ ++ /* no previous data, compute theoritical value from interrupt */ ++ if (ts->timestamp == 0) { ++ /* elapsed time: sensor period * sensor samples number */ ++ interval = (int64_t)ts->period * (int64_t)sensor_nb; ++ ts->timestamp = it->up - interval; ++ return; ++ } ++ ++ /* if interrupt interval is valid, sync with interrupt timestamp */ ++ if (valid) { ++ /* compute measured fifo_period */ ++ fifo_period = fifo_mult * ts->chip_period.val; ++ /* delta time between last sample and last interrupt */ ++ delta = it->lo - ts->timestamp; ++ /* if there are multiple samples, go back to first one */ ++ while (delta >= (fifo_period * 3 / 2)) ++ delta -= fifo_period; ++ /* compute maximal adjustment value */ ++ m = INV_ICM42600_TIMESTAMP_MAX_PERIOD(ts->period) - ts->period; ++ if (delta > m) ++ delta = m; ++ else if (delta < -m) ++ delta = -m; ++ ts->timestamp += delta; ++ } ++} ++EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP); ++ ++void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, ++ uint32_t fifo_period, size_t fifo_nb, ++ unsigned int fifo_no) ++{ ++ int64_t interval; ++ uint32_t fifo_mult; ++ ++ if (ts->new_mult == 0) ++ return; ++ ++ /* update to new multiplier and update period */ ++ ts->mult = ts->new_mult; ++ ts->new_mult = 0; ++ ts->period = ts->mult * ts->chip_period.val; ++ ++ /* ++ * After ODR change the time interval with the previous sample is ++ * undertermined (depends when the change occures). So we compute the ++ * timestamp from the current interrupt using the new FIFO period, the ++ * total number of samples and the current sample numero. ++ */ ++ if (ts->timestamp != 0) { ++ /* compute measured fifo period */ ++ fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD; ++ fifo_period = fifo_mult * ts->chip_period.val; ++ /* computes time interval between interrupt and this sample */ ++ interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period; ++ ts->timestamp = ts->it.up - interval; ++ } ++} ++EXPORT_SYMBOL_NS_GPL(inv_icm42600_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP); ++ ++MODULE_AUTHOR("InvenSense, Inc."); ++MODULE_DESCRIPTION("InvenSense sensors timestamp module"); ++MODULE_LICENSE("GPL"); +--- a/drivers/iio/imu/inv_icm42600/Kconfig ++++ b/drivers/iio/imu/inv_icm42600/Kconfig +@@ -3,6 +3,7 @@ + config INV_ICM42600 + tristate + select IIO_BUFFER ++ select IIO_INV_SENSORS_TIMESTAMP + + config INV_ICM42600_I2C + tristate "InvenSense ICM-426xx I2C driver" +--- a/drivers/iio/imu/inv_icm42600/Makefile ++++ b/drivers/iio/imu/inv_icm42600/Makefile +@@ -6,7 +6,6 @@ inv-icm42600-y += inv_icm42600_gyro.o + inv-icm42600-y += inv_icm42600_accel.o + inv-icm42600-y += inv_icm42600_temp.o + inv-icm42600-y += inv_icm42600_buffer.o +-inv-icm42600-y += inv_icm42600_timestamp.o + + obj-$(CONFIG_INV_ICM42600_I2C) += inv-icm42600-i2c.o + inv-icm42600-i2c-y += inv_icm42600_i2c.o +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +@@ -10,14 +10,15 @@ + #include + #include + #include +-#include ++ + #include ++#include ++#include + #include + + #include "inv_icm42600.h" + #include "inv_icm42600_temp.h" + #include "inv_icm42600_buffer.h" +-#include "inv_icm42600_timestamp.h" + + #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \ + { \ +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +@@ -9,11 +9,12 @@ + #include + #include + #include +-#include ++ + #include ++#include ++#include + + #include "inv_icm42600.h" +-#include "inv_icm42600_timestamp.h" + #include "inv_icm42600_buffer.h" + + /* FIFO header: 1 byte */ +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +@@ -15,11 +15,12 @@ + #include + #include + #include ++ ++#include + #include + + #include "inv_icm42600.h" + #include "inv_icm42600_buffer.h" +-#include "inv_icm42600_timestamp.h" + + static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = { + { +@@ -798,3 +799,4 @@ EXPORT_SYMBOL_GPL(inv_icm42600_pm_ops); + MODULE_AUTHOR("InvenSense, Inc."); + MODULE_DESCRIPTION("InvenSense ICM-426xx device driver"); + MODULE_LICENSE("GPL"); ++MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP); +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c ++++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +@@ -10,14 +10,15 @@ + #include + #include + #include +-#include ++ + #include ++#include ++#include + #include + + #include "inv_icm42600.h" + #include "inv_icm42600_temp.h" + #include "inv_icm42600_buffer.h" +-#include "inv_icm42600_timestamp.h" + + #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \ + { \ +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c ++++ /dev/null +@@ -1,183 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0-or-later +-/* +- * Copyright (C) 2020 Invensense, Inc. +- */ +- +-#include +-#include +-#include +- +-#include "inv_icm42600_timestamp.h" +- +-/* internal chip period is 32kHz, 31250ns */ +-#define INV_ICM42600_TIMESTAMP_PERIOD 31250 +-/* allow a jitter of +/- 2% */ +-#define INV_ICM42600_TIMESTAMP_JITTER 2 +-/* compute min and max periods accepted */ +-#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p) \ +- (((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100) +-#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p) \ +- (((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100) +- +-/* Add a new value inside an accumulator and update the estimate value */ +-static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val) +-{ +- uint64_t sum = 0; +- size_t i; +- +- acc->values[acc->idx++] = val; +- if (acc->idx >= ARRAY_SIZE(acc->values)) +- acc->idx = 0; +- +- /* compute the mean of all stored values, use 0 as empty slot */ +- for (i = 0; i < ARRAY_SIZE(acc->values); ++i) { +- if (acc->values[i] == 0) +- break; +- sum += acc->values[i]; +- } +- +- acc->val = div_u64(sum, i); +-} +- +-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, +- uint32_t period) +-{ +- /* initial odr for sensor after reset is 1kHz */ +- const uint32_t default_period = 1000000; +- +- /* current multiplier and period values after reset */ +- ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD; +- ts->period = default_period; +- /* new set multiplier is the one from chip initialization */ +- ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD; +- +- /* use theoretical value for chip period */ +- inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD); +-} +- +-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, +- uint32_t period, bool fifo) +-{ +- /* when FIFO is on, prevent odr change if one is already pending */ +- if (fifo && ts->new_mult != 0) +- return -EAGAIN; +- +- ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD; +- +- return 0; +-} +- +-static bool inv_validate_period(uint32_t period, uint32_t mult) +-{ +- const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD; +- uint32_t period_min, period_max; +- +- /* check that period is acceptable */ +- period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult; +- period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult; +- if (period > period_min && period < period_max) +- return true; +- else +- return false; +-} +- +-static bool inv_compute_chip_period(struct inv_icm42600_timestamp *ts, +- uint32_t mult, uint32_t period) +-{ +- uint32_t new_chip_period; +- +- if (!inv_validate_period(period, mult)) +- return false; +- +- /* update chip internal period estimation */ +- new_chip_period = period / mult; +- inv_update_acc(&ts->chip_period, new_chip_period); +- +- return true; +-} +- +-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- size_t sensor_nb, int64_t timestamp) +-{ +- struct inv_icm42600_timestamp_interval *it; +- int64_t delta, interval; +- const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD; +- uint32_t period = ts->period; +- int32_t m; +- bool valid = false; +- +- if (fifo_nb == 0) +- return; +- +- /* update interrupt timestamp and compute chip and sensor periods */ +- it = &ts->it; +- it->lo = it->up; +- it->up = timestamp; +- delta = it->up - it->lo; +- if (it->lo != 0) { +- /* compute period: delta time divided by number of samples */ +- period = div_s64(delta, fifo_nb); +- valid = inv_compute_chip_period(ts, fifo_mult, period); +- /* update sensor period if chip internal period is updated */ +- if (valid) +- ts->period = ts->mult * ts->chip_period.val; +- } +- +- /* no previous data, compute theoritical value from interrupt */ +- if (ts->timestamp == 0) { +- /* elapsed time: sensor period * sensor samples number */ +- interval = (int64_t)ts->period * (int64_t)sensor_nb; +- ts->timestamp = it->up - interval; +- return; +- } +- +- /* if interrupt interval is valid, sync with interrupt timestamp */ +- if (valid) { +- /* compute measured fifo_period */ +- fifo_period = fifo_mult * ts->chip_period.val; +- /* delta time between last sample and last interrupt */ +- delta = it->lo - ts->timestamp; +- /* if there are multiple samples, go back to first one */ +- while (delta >= (fifo_period * 3 / 2)) +- delta -= fifo_period; +- /* compute maximal adjustment value */ +- m = INV_ICM42600_TIMESTAMP_MAX_PERIOD(ts->period) - ts->period; +- if (delta > m) +- delta = m; +- else if (delta < -m) +- delta = -m; +- ts->timestamp += delta; +- } +-} +- +-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- unsigned int fifo_no) +-{ +- int64_t interval; +- uint32_t fifo_mult; +- +- if (ts->new_mult == 0) +- return; +- +- /* update to new multiplier and update period */ +- ts->mult = ts->new_mult; +- ts->new_mult = 0; +- ts->period = ts->mult * ts->chip_period.val; +- +- /* +- * After ODR change the time interval with the previous sample is +- * undertermined (depends when the change occures). So we compute the +- * timestamp from the current interrupt using the new FIFO period, the +- * total number of samples and the current sample numero. +- */ +- if (ts->timestamp != 0) { +- /* compute measured fifo period */ +- fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD; +- fifo_period = fifo_mult * ts->chip_period.val; +- /* computes time interval between interrupt and this sample */ +- interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period; +- ts->timestamp = ts->it.up - interval; +- } +-} +--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h ++++ /dev/null +@@ -1,79 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-or-later */ +-/* +- * Copyright (C) 2020 Invensense, Inc. +- */ +- +-#ifndef INV_ICM42600_TIMESTAMP_H_ +-#define INV_ICM42600_TIMESTAMP_H_ +- +-/** +- * struct inv_icm42600_timestamp_interval - timestamps interval +- * @lo: interval lower bound +- * @up: interval upper bound +- */ +-struct inv_icm42600_timestamp_interval { +- int64_t lo; +- int64_t up; +-}; +- +-/** +- * struct inv_icm42600_timestamp_acc - accumulator for computing an estimation +- * @val: current estimation of the value, the mean of all values +- * @idx: current index of the next free place in values table +- * @values: table of all measured values, use for computing the mean +- */ +-struct inv_icm42600_timestamp_acc { +- uint32_t val; +- size_t idx; +- uint32_t values[32]; +-}; +- +-/** +- * struct inv_icm42600_timestamp - timestamp management states +- * @it: interrupts interval timestamps +- * @timestamp: store last timestamp for computing next data timestamp +- * @mult: current internal period multiplier +- * @new_mult: new set internal period multiplier (not yet effective) +- * @period: measured current period of the sensor +- * @chip_period: accumulator for computing internal chip period +- */ +-struct inv_icm42600_timestamp { +- struct inv_icm42600_timestamp_interval it; +- int64_t timestamp; +- uint32_t mult; +- uint32_t new_mult; +- uint32_t period; +- struct inv_icm42600_timestamp_acc chip_period; +-}; +- +-void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, +- uint32_t period); +- +-int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, +- uint32_t period, bool fifo); +- +-void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- size_t sensor_nb, int64_t timestamp); +- +-static inline int64_t +-inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts) +-{ +- ts->timestamp += ts->period; +- return ts->timestamp; +-} +- +-void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, +- uint32_t fifo_period, size_t fifo_nb, +- unsigned int fifo_no); +- +-static inline void +-inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts) +-{ +- const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL}; +- +- ts->it = interval_init; +- ts->timestamp = 0; +-} +- +-#endif +--- /dev/null ++++ b/include/linux/iio/common/inv_icm42600_timestamp.h +@@ -0,0 +1,79 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++/* ++ * Copyright (C) 2020 Invensense, Inc. ++ */ ++ ++#ifndef INV_ICM42600_TIMESTAMP_H_ ++#define INV_ICM42600_TIMESTAMP_H_ ++ ++/** ++ * struct inv_icm42600_timestamp_interval - timestamps interval ++ * @lo: interval lower bound ++ * @up: interval upper bound ++ */ ++struct inv_icm42600_timestamp_interval { ++ int64_t lo; ++ int64_t up; ++}; ++ ++/** ++ * struct inv_icm42600_timestamp_acc - accumulator for computing an estimation ++ * @val: current estimation of the value, the mean of all values ++ * @idx: current index of the next free place in values table ++ * @values: table of all measured values, use for computing the mean ++ */ ++struct inv_icm42600_timestamp_acc { ++ uint32_t val; ++ size_t idx; ++ uint32_t values[32]; ++}; ++ ++/** ++ * struct inv_icm42600_timestamp - timestamp management states ++ * @it: interrupts interval timestamps ++ * @timestamp: store last timestamp for computing next data timestamp ++ * @mult: current internal period multiplier ++ * @new_mult: new set internal period multiplier (not yet effective) ++ * @period: measured current period of the sensor ++ * @chip_period: accumulator for computing internal chip period ++ */ ++struct inv_icm42600_timestamp { ++ struct inv_icm42600_timestamp_interval it; ++ int64_t timestamp; ++ uint32_t mult; ++ uint32_t new_mult; ++ uint32_t period; ++ struct inv_icm42600_timestamp_acc chip_period; ++}; ++ ++void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, ++ uint32_t period); ++ ++int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, ++ uint32_t period, bool fifo); ++ ++void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, ++ uint32_t fifo_period, size_t fifo_nb, ++ size_t sensor_nb, int64_t timestamp); ++ ++static inline int64_t ++inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts) ++{ ++ ts->timestamp += ts->period; ++ return ts->timestamp; ++} ++ ++void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, ++ uint32_t fifo_period, size_t fifo_nb, ++ unsigned int fifo_no); ++ ++static inline void ++inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts) ++{ ++ const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL}; ++ ++ ts->it = interval_init; ++ ts->timestamp = 0; ++} ++ ++#endif diff --git a/queue-5.15/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch b/queue-5.15/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch new file mode 100644 index 0000000000..a56dad0226 --- /dev/null +++ b/queue-5.15/ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch @@ -0,0 +1,256 @@ +From stable+bounces-276810-greg=kroah.com@vger.kernel.org Thu Jul 16 20:31:32 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 14:22:04 -0400 +Subject: ksmbd: centralize ksmbd_conn final release to plug transport leak +To: stable@vger.kernel.org +Cc: DaeMyung Kang , Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260716182205.921233-6-sashal@kernel.org> + +From: DaeMyung Kang + +[ 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 +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/connection.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- + fs/ksmbd/connection.h | 3 +++ + fs/ksmbd/oplock.c | 4 ++-- + fs/ksmbd/vfs_cache.c | 18 +++++++++++++++++- + 4 files changed, 69 insertions(+), 5 deletions(-) + +--- a/fs/ksmbd/connection.c ++++ b/fs/ksmbd/connection.c +@@ -23,6 +23,43 @@ LIST_HEAD(conn_list); + DECLARE_RWSEM(conn_list_lock); + + /** ++ * 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, perform the ++ * final, once-per-struct release of a ksmbd_conn. ++ * ++ * The transport is not released here: free_transport() releases it after ++ * calling ksmbd_conn_free(). The final release therefore only pairs ++ * ida_destroy() with kfree(), reaches no sleeping teardown and is safe ++ * from any context, including the RCU callback that frees an opinfo ++ * holding a connection reference. ++ */ ++void ksmbd_conn_put(struct ksmbd_conn *conn) ++{ ++ if (!conn) ++ return; ++ ++ if (atomic_dec_and_test(&conn->refcnt)) { ++ ida_destroy(&conn->async_ida); ++ kfree(conn); ++ } ++} ++ ++/** + * ksmbd_conn_free() - free resources of the connection instance + * + * @conn: connection instance to be cleand up +@@ -36,12 +73,19 @@ void ksmbd_conn_free(struct ksmbd_conn * + list_del(&conn->conns_list); + 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); +- ida_destroy(&conn->async_ida); +- kfree(conn); ++ ksmbd_conn_put(conn); + } + + /** +@@ -70,6 +114,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void + conn->um = NULL; + atomic_set(&conn->req_running, 0); + atomic_set(&conn->r_count, 0); ++ atomic_set(&conn->refcnt, 1); + conn->total_credits = 1; + conn->outstanding_credits = 0; + +--- a/fs/ksmbd/connection.h ++++ b/fs/ksmbd/connection.h +@@ -106,6 +106,7 @@ struct ksmbd_conn { + bool signing_negotiated; + __le16 signing_algorithm; + bool binding; ++ atomic_t refcnt; + }; + + struct ksmbd_conn_ops { +@@ -148,6 +149,8 @@ bool ksmbd_conn_alive(struct ksmbd_conn + void ksmbd_conn_wait_idle(struct ksmbd_conn *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); + 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/ksmbd/oplock.c ++++ b/fs/ksmbd/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; +@@ -124,6 +123,7 @@ static void free_opinfo(struct oplock_in + { + if (opinfo->is_lease) + free_lease(opinfo); ++ ksmbd_conn_put(opinfo->conn); + kfree(opinfo); + } + +--- a/fs/ksmbd/vfs_cache.c ++++ b/fs/ksmbd/vfs_cache.c +@@ -328,6 +328,14 @@ static void __ksmbd_close_fd(struct ksmb + kfree(smb_lock); + } + ++ /* ++ * Drop fp's strong reference on conn (taken in ksmbd_open_fd()). ++ */ ++ if (fp->conn) { ++ ksmbd_conn_put(fp->conn); ++ fp->conn = NULL; ++ } ++ + if (ksmbd_stream_fd(fp)) + kfree(fp->stream.name); + kmem_cache_free(filp_cache, fp); +@@ -579,7 +587,13 @@ 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 __ksmbd_close_fd() never dereferences a dangling ++ * pointer. Paired with ksmbd_conn_put() 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; +@@ -601,6 +615,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); + } diff --git a/queue-5.15/ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch b/queue-5.15/ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch new file mode 100644 index 0000000000..1483ac8f93 --- /dev/null +++ b/queue-5.15/ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch @@ -0,0 +1,46 @@ +From stable+bounces-276808-greg=kroah.com@vger.kernel.org Thu Jul 16 20:29:06 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 14:22:03 -0400 +Subject: ksmbd: destroy async_ida in ksmbd_conn_free() +To: stable@vger.kernel.org +Cc: DaeMyung Kang , Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260716182205.921233-5-sashal@kernel.org> + +From: DaeMyung Kang + +[ Upstream commit b32c8db48212a34998c36d0bbc05b29d5c407ef5 ] + +When per-connection async_ida was converted from a dynamically +allocated ksmbd_ida to an embedded struct ida, ksmbd_ida_free() was +removed from the connection teardown path but no matching +ida_destroy() was added. The connection is therefore freed with the +IDA's backing xarray still intact. + +The kernel IDA API expects ida_init() and ida_destroy() to be paired +over an object's lifetime, so add the missing cleanup before the +connection is freed. + +No leak has been observed in testing; this is a pairing fix to match +the IDA lifetime rules, not a response to a reproduced regression. + +Fixes: d40012a83f87 ("cifsd: declare ida statically") +Signed-off-by: DaeMyung Kang +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/connection.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/ksmbd/connection.c ++++ b/fs/ksmbd/connection.c +@@ -40,6 +40,7 @@ void ksmbd_conn_free(struct ksmbd_conn * + kvfree(conn->request_buf); + kfree(conn->preauth_info); + kfree(conn->mechToken); ++ ida_destroy(&conn->async_ida); + kfree(conn); + } + diff --git a/queue-5.15/ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch b/queue-5.15/ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch new file mode 100644 index 0000000000..cd205cad51 --- /dev/null +++ b/queue-5.15/ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch @@ -0,0 +1,79 @@ +From sashal@kernel.org Thu Jul 16 20:22:11 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 14:22:02 -0400 +Subject: ksmbd: fix mechToken leak when SPNEGO decode fails after token alloc +To: stable@vger.kernel.org +Cc: Greg Kroah-Hartman , Namjae Jeon , Steve French , Sergey Senozhatsky , Tom Talpey , linux-cifs@vger.kernel.org, stable@kernel.org, Steve French , Sasha Levin +Message-ID: <20260716182205.921233-4-sashal@kernel.org> + +From: Greg Kroah-Hartman + +[ Upstream commit ad0057fb91218914d6c98268718ceb9d59b388e1 ] + +The kernel ASN.1 BER decoder calls action callbacks incrementally as it +walks the input. When ksmbd_decode_negTokenInit() reaches the mechToken +[2] OCTET STRING element, ksmbd_neg_token_alloc() allocates +conn->mechToken immediately via kmemdup_nul(). If a later element in +the same blob is malformed, then the decoder will return nonzero after +the allocation is already live. This could happen if mechListMIC [3] +overrunse the enclosing SEQUENCE. + +decode_negotiation_token() then sets conn->use_spnego = false because +both the negTokenInit and negTokenTarg grammars failed. The cleanup at +the bottom of smb2_sess_setup() is gated on use_spnego: + + if (conn->use_spnego && conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } + +so the kfree is skipped, causing the mechToken to never be freed. + +This codepath is reachable pre-authentication, so untrusted clients can +cause slow memory leaks on a server without even being properly +authenticated. + +Fix this up by not checking check for use_spnego, as it's not required, +so the memory will always be properly freed. At the same time, always +free the memory in ksmbd_conn_free() incase some other failure path +forgot to free it. + +Cc: Namjae Jeon +Cc: Steve French +Cc: Sergey Senozhatsky +Cc: Tom Talpey +Cc: linux-cifs@vger.kernel.org +Cc: +Assisted-by: gregkh_clanker_t1000 +Signed-off-by: Greg Kroah-Hartman +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/connection.c | 1 + + fs/ksmbd/smb2pdu.c | 2 +- + 2 files changed, 2 insertions(+), 1 deletion(-) + +--- a/fs/ksmbd/connection.c ++++ b/fs/ksmbd/connection.c +@@ -39,6 +39,7 @@ void ksmbd_conn_free(struct ksmbd_conn * + xa_destroy(&conn->sessions); + kvfree(conn->request_buf); + kfree(conn->preauth_info); ++ kfree(conn->mechToken); + kfree(conn); + } + +--- a/fs/ksmbd/smb2pdu.c ++++ b/fs/ksmbd/smb2pdu.c +@@ -1920,7 +1920,7 @@ out_err: + else if (rc) + rsp->hdr.Status = STATUS_LOGON_FAILURE; + +- if (conn->use_spnego && conn->mechToken) { ++ if (conn->mechToken) { + kfree(conn->mechToken); + conn->mechToken = NULL; + } diff --git a/queue-5.15/ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch b/queue-5.15/ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch new file mode 100644 index 0000000000..07bd7f9118 --- /dev/null +++ b/queue-5.15/ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch @@ -0,0 +1,67 @@ +From stable+bounces-276806-greg=kroah.com@vger.kernel.org Thu Jul 16 20:29:01 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 14:22:01 -0400 +Subject: ksmbd: fix use-after-free in __ksmbd_close_fd() via durable scavenger +To: stable@vger.kernel.org +Cc: Namjae Jeon , munan Huang , ChenXiaoSong , Steve French , Sasha Levin +Message-ID: <20260716182205.921233-3-sashal@kernel.org> + +From: Namjae Jeon + +[ Upstream commit 235e32320a470fcd3998fb3774f2290a0eb302a1 ] + +When a durable file handle survives session disconnect (TCP close without +SMB2_LOGOFF), session_fd_check() sets fp->conn = NULL to preserve the +handle for later reconnection. However, it did not clean up the byte-range +locks on fp->lock_list. + +Later, when the durable scavenger thread times out and calls +__ksmbd_close_fd(NULL, fp), the lock cleanup loop did: + + spin_lock(&fp->conn->llist_lock); + +This caused a slab use-after-free because fp->conn was NULL and the +original connection object had already been freed by +ksmbd_tcp_disconnect(). + +The root cause is asymmetric cleanup: lock entries (smb_lock->clist) were +left dangling on the freed conn->lock_list while fp->conn was nulled out. + +To fix this issue properly, we need to handle the lifetime of +smb_lock->clist across three paths: + - Safely skip clist deletion when list is empty and fp->conn is NULL. + - Remove the lock from the old connection's lock_list in + session_fd_check() + - Re-add the lock to the new connection's lock_list in + ksmbd_reopen_durable_fd(). + +Fixes: c8efcc786146 ("ksmbd: add support for durable handles v1/v2") +Co-developed-by: munan Huang +Signed-off-by: munan Huang +Reviewed-by: ChenXiaoSong +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: c1016dd1d8b2 ("ksmbd: track the connection owning a byte-range lock") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/vfs_cache.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/fs/ksmbd/vfs_cache.c ++++ b/fs/ksmbd/vfs_cache.c +@@ -317,9 +317,11 @@ 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) { +- spin_lock(&fp->conn->llist_lock); +- list_del(&smb_lock->clist); +- spin_unlock(&fp->conn->llist_lock); ++ 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); ++ } + + list_del(&smb_lock->flist); + locks_free_lock(smb_lock->fl); diff --git a/queue-5.15/ksmbd-track-the-connection-owning-a-byte-range-lock.patch b/queue-5.15/ksmbd-track-the-connection-owning-a-byte-range-lock.patch new file mode 100644 index 0000000000..2026d56ec7 --- /dev/null +++ b/queue-5.15/ksmbd-track-the-connection-owning-a-byte-range-lock.patch @@ -0,0 +1,112 @@ +From stable+bounces-276811-greg=kroah.com@vger.kernel.org Thu Jul 16 20:39:31 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 14:22:05 -0400 +Subject: ksmbd: track the connection owning a byte-range lock +To: stable@vger.kernel.org +Cc: Namjae Jeon , Musaab Khan , Steve French , Sasha Levin +Message-ID: <20260716182205.921233-7-sashal@kernel.org> + +From: Namjae Jeon + +[ 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 +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/smb2pdu.c | 10 ++++++++-- + fs/ksmbd/vfs_cache.c | 12 ++++++++---- + fs/ksmbd/vfs_cache.h | 1 + + 3 files changed, 17 insertions(+), 6 deletions(-) + +--- a/fs/ksmbd/smb2pdu.c ++++ b/fs/ksmbd/smb2pdu.c +@@ -7074,9 +7074,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; +@@ -7212,6 +7214,7 @@ skip: + goto retry; + } 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); +@@ -7259,11 +7262,14 @@ out: + pr_err("rollback unlock fail : %d\n", rc); + + 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); + locks_free_lock(rlock); +--- a/fs/ksmbd/vfs_cache.c ++++ b/fs/ksmbd/vfs_cache.c +@@ -317,10 +317,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); +--- a/fs/ksmbd/vfs_cache.h ++++ b/fs/ksmbd/vfs_cache.h +@@ -30,6 +30,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-5.15/leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch b/queue-5.15/leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch new file mode 100644 index 0000000000..4347cad5ac --- /dev/null +++ b/queue-5.15/leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch @@ -0,0 +1,54 @@ +From stable+bounces-277530-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:48 2026 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:25:35 -0400 +Subject: leds: lm3601x: Improve error reporting for problems during .remove() +To: stable@vger.kernel.org +Cc: "Uwe Kleine-König" , "Pavel Machek" , "Wolfram Sang" , "Sasha Levin" +Message-ID: <20260720022537.2927284-3-sashal@kernel.org> + +From: Uwe Kleine-König + +[ Upstream commit 22a23436891886a66d21af3619f4a4e8809f0e0a ] + +Returning an error value in an i2c remove callback results in a generic +error message being emitted by the i2c core, but otherwise it doesn't +make a difference. The device goes away anyhow and the devm cleanups are +called. + +So instead of triggering the generic i2c error message, emit a more +helpful message if a problem occurs and return 0 to suppress the generic +message. + +This patch is a preparation for making i2c remove callbacks return void. + +Acked-by: Pavel Machek +Signed-off-by: Uwe Kleine-König +Signed-off-by: Wolfram Sang +Stable-dep-of: 1f0bdc2884b6 ("usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/leds/flash/leds-lm3601x.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +--- a/drivers/leds/flash/leds-lm3601x.c ++++ b/drivers/leds/flash/leds-lm3601x.c +@@ -443,10 +443,15 @@ static int lm3601x_probe(struct i2c_clie + static int lm3601x_remove(struct i2c_client *client) + { + struct lm3601x_led *led = i2c_get_clientdata(client); ++ int ret; + +- return regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, +- LM3601X_ENABLE_MASK, +- LM3601X_MODE_STANDBY); ++ ret = regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, ++ LM3601X_ENABLE_MASK, LM3601X_MODE_STANDBY); ++ if (ret) ++ dev_warn(&client->dev, ++ "Failed to put into standby (%pe)\n", ERR_PTR(ret)); ++ ++ return 0; + } + + static const struct i2c_device_id lm3601x_id[] = { diff --git a/queue-5.15/leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch b/queue-5.15/leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch new file mode 100644 index 0000000000..33d0f76eb8 --- /dev/null +++ b/queue-5.15/leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch @@ -0,0 +1,48 @@ +From stable+bounces-277529-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:47 2026 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:25:34 -0400 +Subject: leds: lm3697: Remove duplicated error reporting in .remove() +To: stable@vger.kernel.org +Cc: "Uwe Kleine-König" , "Pavel Machek" , "Wolfram Sang" , "Sasha Levin" +Message-ID: <20260720022537.2927284-2-sashal@kernel.org> + +From: Uwe Kleine-König + +[ Upstream commit af89fa11fae1cee4e07453f780e6e5573ef0f477 ] + +Returning an error value from an i2c remove callback results in an error +message being emitted by the i2c core, but otherwise it doesn't make a +difference. The device goes away anyhow and the devm cleanups are +called. + +As lm3697_remove() already emits an error message on failure and the +additional error message by the i2c core doesn't add any useful +information, don't pass the error value up the stack. Instead continue +to clean up and return 0. + +This patch is a preparation for making i2c remove callbacks return void. + +Acked-by: Pavel Machek +Signed-off-by: Uwe Kleine-König +Signed-off-by: Wolfram Sang +Stable-dep-of: 1f0bdc2884b6 ("usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/leds/leds-lm3697.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/leds/leds-lm3697.c ++++ b/drivers/leds/leds-lm3697.c +@@ -345,10 +345,8 @@ static int lm3697_remove(struct i2c_clie + + ret = regmap_update_bits(led->regmap, LM3697_CTRL_ENABLE, + LM3697_CTRL_A_B_EN, 0); +- if (ret) { ++ if (ret) + dev_err(dev, "Failed to disable the device\n"); +- return ret; +- } + + if (led->enable_gpio) + gpiod_direction_output(led->enable_gpio, 0); diff --git a/queue-5.15/netfilter-ebtables-use-vmalloc_array-to-improve-code.patch b/queue-5.15/netfilter-ebtables-use-vmalloc_array-to-improve-code.patch new file mode 100644 index 0000000000..2c64bee0ba --- /dev/null +++ b/queue-5.15/netfilter-ebtables-use-vmalloc_array-to-improve-code.patch @@ -0,0 +1,77 @@ +From stable+bounces-275053-greg=kroah.com@vger.kernel.org Wed Jul 15 23:26:53 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 17:26:45 -0400 +Subject: netfilter: ebtables: Use vmalloc_array() to improve code +To: stable@vger.kernel.org +Cc: Qianfeng Rong , Florian Westphal , Sasha Levin +Message-ID: <20260715212646.278719-1-sashal@kernel.org> + +From: Qianfeng Rong + +[ Upstream commit 46015e6b3ea75297b28d4806564f3f692cf11861 ] + +Remove array_size() calls and replace vmalloc() with vmalloc_array() to +simplify the code. vmalloc_array() is also optimized better, uses fewer +instructions, and handles overflow more concisely[1]. + +[1]: https://lore.kernel.org/lkml/abc66ec5-85a4-47e1-9759-2f60ab111971@vivo.com/ +Signed-off-by: Qianfeng Rong +Signed-off-by: Florian Westphal +Stable-dep-of: cbfe53599eeb ("netfilter: ebtables: zero chainstack array") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bridge/netfilter/ebtables.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -923,8 +923,8 @@ static int translate_table(struct net *n + * if an error occurs + */ + newinfo->chainstack = +- vmalloc(array_size(nr_cpu_ids, +- sizeof(*(newinfo->chainstack)))); ++ vmalloc_array(nr_cpu_ids, ++ sizeof(*(newinfo->chainstack))); + if (!newinfo->chainstack) + return -ENOMEM; + for_each_possible_cpu(i) { +@@ -941,7 +941,7 @@ static int translate_table(struct net *n + } + } + +- cl_s = vmalloc(array_size(udc_cnt, sizeof(*cl_s))); ++ cl_s = vmalloc_array(udc_cnt, sizeof(*cl_s)); + if (!cl_s) + return -ENOMEM; + i = 0; /* the i'th udc */ +@@ -1021,8 +1021,8 @@ static int do_replace_finish(struct net + * the check on the size is done later, when we have the lock + */ + if (repl->num_counters) { +- unsigned long size = repl->num_counters * sizeof(*counterstmp); +- counterstmp = vmalloc(size); ++ counterstmp = vmalloc_array(repl->num_counters, ++ sizeof(*counterstmp)); + if (!counterstmp) + return -ENOMEM; + } +@@ -1389,7 +1389,7 @@ static int do_update_counters(struct net + if (num_counters == 0) + return -EINVAL; + +- tmp = vmalloc(array_size(num_counters, sizeof(*tmp))); ++ tmp = vmalloc_array(num_counters, sizeof(*tmp)); + if (!tmp) + return -ENOMEM; + +@@ -1530,7 +1530,7 @@ static int copy_counters_to_user(struct + if (num_counters != nentries) + return -EINVAL; + +- counterstmp = vmalloc(array_size(nentries, sizeof(*counterstmp))); ++ counterstmp = vmalloc_array(nentries, sizeof(*counterstmp)); + if (!counterstmp) + return -ENOMEM; + diff --git a/queue-5.15/netfilter-ebtables-zero-chainstack-array.patch b/queue-5.15/netfilter-ebtables-zero-chainstack-array.patch new file mode 100644 index 0000000000..05eb469745 --- /dev/null +++ b/queue-5.15/netfilter-ebtables-zero-chainstack-array.patch @@ -0,0 +1,46 @@ +From stable+bounces-275054-greg=kroah.com@vger.kernel.org Wed Jul 15 23:26:54 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 17:26:46 -0400 +Subject: netfilter: ebtables: zero chainstack array +To: stable@vger.kernel.org +Cc: Florian Westphal , Sasha Levin +Message-ID: <20260715212646.278719-2-sashal@kernel.org> + +From: Florian Westphal + +[ Upstream commit cbfe53599eebffd188938ab6774cc41794f6f9d5 ] + +sashiko reports: + looking at ebtables table + translation, could a sparse cpu_possible_mask lead to an uninitialized pointer + free? + + If cpu_possible_mask is sparse (for example, CPU 0 and CPU 2 are possible, + but CPU 1 is not), the allocation loop skips CPU 1. If vmalloc_node() fails at + CPU 2, the cleanup loop will blindly decrement and call vfree() on + newinfo->chainstack[1]. + +Not a real-world bug, such allocation isn't expected to fail +in the first place. + +Cc: stable@vger.kernel.org +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Florian Westphal +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bridge/netfilter/ebtables.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +--- a/net/bridge/netfilter/ebtables.c ++++ b/net/bridge/netfilter/ebtables.c +@@ -923,8 +923,7 @@ static int translate_table(struct net *n + * if an error occurs + */ + newinfo->chainstack = +- vmalloc_array(nr_cpu_ids, +- sizeof(*(newinfo->chainstack))); ++ vcalloc(nr_cpu_ids, sizeof(*(newinfo->chainstack))); + if (!newinfo->chainstack) + return -ENOMEM; + for_each_possible_cpu(i) { diff --git a/queue-5.15/pci-add-kerneldoc-for-pci_resize_resource.patch b/queue-5.15/pci-add-kerneldoc-for-pci_resize_resource.patch new file mode 100644 index 0000000000..0bbdc100c0 --- /dev/null +++ b/queue-5.15/pci-add-kerneldoc-for-pci_resize_resource.patch @@ -0,0 +1,55 @@ +From stable+bounces-274894-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:31 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 07:16:16 -0400 +Subject: PCI: Add kerneldoc for pci_resize_resource() +To: stable@vger.kernel.org +Cc: "Ilpo Järvinen" , "Bjorn Helgaas" , "Alex Bennée" , "Sasha Levin" +Message-ID: <20260715111618.647249-4-sashal@kernel.org> + +From: Ilpo Järvinen + +[ 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 +Signed-off-by: Bjorn Helgaas +Tested-by: Alex Bennée # 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 +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/setup-res.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +--- a/drivers/pci/setup-res.c ++++ b/drivers/pci/setup-res.c +@@ -422,6 +422,26 @@ void pci_release_resource(struct pci_dev + } + EXPORT_SYMBOL(pci_release_resource); + ++/** ++ * 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-5.15/pci-altera-fix-resource-leaks-on-probe-failure.patch b/queue-5.15/pci-altera-fix-resource-leaks-on-probe-failure.patch new file mode 100644 index 0000000000..e394f25e5c --- /dev/null +++ b/queue-5.15/pci-altera-fix-resource-leaks-on-probe-failure.patch @@ -0,0 +1,99 @@ +From stable+bounces-274467-greg=kroah.com@vger.kernel.org Tue Jul 14 18:55:56 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 12:55:48 -0400 +Subject: PCI: altera: Fix resource leaks on probe failure +To: stable@vger.kernel.org +Cc: Mahesh Vaidya , Manivannan Sadhasivam , "Subhransu S. Prusty" , Sasha Levin +Message-ID: <20260714165548.2885119-1-sashal@kernel.org> + +From: Mahesh Vaidya + +[ Upstream commit 7a94138caeb27f3c49c1dbd93bf422098925bb28 ] + +The chained IRQ handler is set during probe, but is only removed during the +driver remove(). If pci_host_probe() fails, the handler and INTx IRQ +domain remain set even though the devm-managed host bridge storage +containing struct altera_pcie will be released, leaving the handler with +a stale data pointer. + +Interrupts are also enabled before pci_host_probe() is called. If probe +fails after that point, the controller interrupt source should be disabled +before the chained handler and INTx domain are removed. + +So set the chained handler only after the INTx domain has been created. +Disable controller interrupts during IRQ teardown, and tear the IRQ setup +down if pci_host_probe() fails. + +Fixes: c63aed7334c2 ("PCI: altera: Use pci_host_probe() to register host") +Signed-off-by: Mahesh Vaidya +[mani: commit log] +Signed-off-by: Manivannan Sadhasivam +Reviewed-by: Subhransu S. Prusty +Cc: stable@vger.kernel.org +Link: https://patch.msgid.link/20260430204330.3121003-3-mahesh.vaidya@altera.com +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/controller/pcie-altera.c | 28 ++++++++++++++++++++++++++-- + 1 file changed, 26 insertions(+), 2 deletions(-) + +--- a/drivers/pci/controller/pcie-altera.c ++++ b/drivers/pci/controller/pcie-altera.c +@@ -683,8 +683,18 @@ static int altera_pcie_init_irq_domain(s + return 0; + } + ++static void altera_pcie_disable_irq(struct altera_pcie *pcie) ++{ ++ if (pcie->pcie_data->version == ALTERA_PCIE_V1 || ++ pcie->pcie_data->version == ALTERA_PCIE_V2) { ++ /* Disable all P2A interrupts */ ++ cra_writel(pcie, 0, P2A_INT_ENABLE); ++ } ++} ++ + static void altera_pcie_irq_teardown(struct altera_pcie *pcie) + { ++ altera_pcie_disable_irq(pcie); + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); + irq_domain_remove(pcie->irq_domain); + } +@@ -709,7 +719,6 @@ static int altera_pcie_parse_dt(struct a + if (pcie->irq < 0) + return pcie->irq; + +- irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie); + return 0; + } + +@@ -794,6 +803,12 @@ static int altera_pcie_probe(struct plat + return ret; + } + ++ /* ++ * The chained handler uses pcie->irq_domain, so set it only after the ++ * INTx domain has been created. ++ */ ++ irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie); ++ + /* clear all interrupts */ + cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS); + /* enable all interrupts */ +@@ -804,7 +819,16 @@ static int altera_pcie_probe(struct plat + bridge->busnr = pcie->root_bus_nr; + bridge->ops = &altera_pcie_ops; + +- return pci_host_probe(bridge); ++ ret = pci_host_probe(bridge); ++ if (ret) ++ goto err_teardown_irq; ++ ++ return 0; ++ ++err_teardown_irq: ++ altera_pcie_irq_teardown(pcie); ++ ++ return ret; + } + + static int altera_pcie_remove(struct platform_device *pdev) diff --git a/queue-5.15/pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch b/queue-5.15/pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch new file mode 100644 index 0000000000..08aa9acfa1 --- /dev/null +++ b/queue-5.15/pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch @@ -0,0 +1,142 @@ +From stable+bounces-274634-greg=kroah.com@vger.kernel.org Wed Jul 15 02:17:59 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 20:17:47 -0400 +Subject: PCI: controller: Use dev_fwnode() instead of of_fwnode_handle() +To: stable@vger.kernel.org +Cc: "Jiri Slaby (SUSE)" , Arnd Bergmann , Bjorn Helgaas , Sasha Levin +Message-ID: <20260715001749.3783652-1-sashal@kernel.org> + +From: "Jiri Slaby (SUSE)" + +[ Upstream commit a103d2dede5683dabbac2c3374bc24b6a9434478 ] + +All irq_domain functions now accept fwnode instead of of_node. But many +PCI controllers still extract dev to of_node and then of_node to fwnode. + +Instead, clean this up and simply use the dev_fwnode() helper to extract +fwnode directly from dev. Internally, it still does dev => of_node => +fwnode steps, but it's now hidden from the users. + +In the case of altera, this also removes an unused 'node' variable that is +only used when CONFIG_OF is enabled: + + drivers/pci/controller/pcie-altera.c: In function 'altera_pcie_init_irq_domain': + drivers/pci/controller/pcie-altera.c:855:29: error: unused variable 'node' [-Werror=unused-variable] + 855 | struct device_node *node = dev->of_node; + +Signed-off-by: Jiri Slaby (SUSE) +Signed-off-by: Arnd Bergmann # altera +[bhelgaas: squash together, rebase to precede msi-parent] +Signed-off-by: Bjorn Helgaas +Link: https://patch.msgid.link/20250521163329.2137973-1-arnd@kernel.org +Link: https://patch.msgid.link/20250611104348.192092-16-jirislaby@kernel.org +Link: https://patch.msgid.link/20250723065907.1841758-1-jirislaby@kernel.org +Stable-dep-of: f865a57896bd ("PCI: mediatek: Fix IRQ domain leak when port fails to enable") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- + drivers/pci/controller/mobiveil/pcie-mobiveil-host.c | 8 +++----- + drivers/pci/controller/pcie-altera-msi.c | 2 +- + drivers/pci/controller/pcie-altera.c | 3 +-- + drivers/pci/controller/pcie-mediatek-gen3.c | 2 +- + drivers/pci/controller/pcie-mediatek.c | 2 +- + drivers/pci/controller/pcie-xilinx-nwl.c | 2 +- + 7 files changed, 9 insertions(+), 12 deletions(-) + +--- a/drivers/pci/controller/dwc/pcie-designware-host.c ++++ b/drivers/pci/controller/dwc/pcie-designware-host.c +@@ -232,7 +232,7 @@ static const struct irq_domain_ops dw_pc + int dw_pcie_allocate_domains(struct pcie_port *pp) + { + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); +- struct fwnode_handle *fwnode = of_node_to_fwnode(pci->dev->of_node); ++ struct fwnode_handle *fwnode = dev_fwnode(pci->dev); + + pp->irq_domain = irq_domain_create_linear(fwnode, pp->num_vectors, + &dw_pcie_msi_domain_ops, pp); +--- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c ++++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +@@ -445,7 +445,7 @@ static const struct irq_domain_ops msi_d + static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie) + { + struct device *dev = &pcie->pdev->dev; +- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); ++ struct fwnode_handle *fwnode = dev_fwnode(dev); + struct mobiveil_msi *msi = &pcie->rp.msi; + + mutex_init(&msi->lock); +@@ -471,13 +471,11 @@ static int mobiveil_allocate_msi_domains + static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie) + { + struct device *dev = &pcie->pdev->dev; +- struct device_node *node = dev->of_node; + struct mobiveil_root_port *rp = &pcie->rp; + + /* setup INTx */ +- rp->intx_domain = irq_domain_add_linear(node, PCI_NUM_INTX, +- &intx_domain_ops, pcie); +- ++ rp->intx_domain = irq_domain_create_linear(dev_fwnode(dev), PCI_NUM_INTX, &intx_domain_ops, ++ pcie); + if (!rp->intx_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return -ENOMEM; +--- a/drivers/pci/controller/pcie-altera-msi.c ++++ b/drivers/pci/controller/pcie-altera-msi.c +@@ -171,7 +171,7 @@ static const struct irq_domain_ops msi_d + + static int altera_allocate_domains(struct altera_msi *msi) + { +- struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node); ++ struct fwnode_handle *fwnode = dev_fwnode(&msi->pdev->dev); + + msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors, + &msi_domain_ops, msi); +--- a/drivers/pci/controller/pcie-altera.c ++++ b/drivers/pci/controller/pcie-altera.c +@@ -670,10 +670,9 @@ static void altera_pcie_isr(struct irq_d + static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) + { + struct device *dev = &pcie->pdev->dev; +- struct device_node *node = dev->of_node; + + /* Setup INTx */ +- pcie->irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX, ++ pcie->irq_domain = irq_domain_create_linear(dev_fwnode(dev), PCI_NUM_INTX, + &intx_domain_ops, pcie); + if (!pcie->irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); +--- a/drivers/pci/controller/pcie-mediatek-gen3.c ++++ b/drivers/pci/controller/pcie-mediatek-gen3.c +@@ -607,7 +607,7 @@ static int mtk_pcie_init_irq_domains(str + /* Setup MSI */ + mutex_init(&port->lock); + +- port->msi_bottom_domain = irq_domain_add_linear(node, PCIE_MSI_IRQS_NUM, ++ port->msi_bottom_domain = irq_domain_create_linear(dev_fwnode(dev), PCIE_MSI_IRQS_NUM, + &mtk_msi_bottom_domain_ops, port); + if (!port->msi_bottom_domain) { + dev_err(dev, "failed to create MSI bottom domain\n"); +--- a/drivers/pci/controller/pcie-mediatek.c ++++ b/drivers/pci/controller/pcie-mediatek.c +@@ -503,7 +503,7 @@ static struct msi_domain_info mtk_msi_do + + static int mtk_pcie_allocate_msi_domains(struct mtk_pcie_port *port) + { +- struct fwnode_handle *fwnode = of_node_to_fwnode(port->pcie->dev->of_node); ++ struct fwnode_handle *fwnode = dev_fwnode(port->pcie->dev); + + mutex_init(&port->lock); + +--- a/drivers/pci/controller/pcie-xilinx-nwl.c ++++ b/drivers/pci/controller/pcie-xilinx-nwl.c +@@ -505,7 +505,7 @@ static int nwl_pcie_init_msi_irq_domain( + { + #ifdef CONFIG_PCI_MSI + struct device *dev = pcie->dev; +- struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); ++ struct fwnode_handle *fwnode = dev_fwnode(dev); + struct nwl_msi *msi = &pcie->msi; + + msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR, diff --git a/queue-5.15/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch b/queue-5.15/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch new file mode 100644 index 0000000000..9fa2c06692 --- /dev/null +++ b/queue-5.15/pci-fix-restoring-bars-on-bar-resize-rollback-path.patch @@ -0,0 +1,336 @@ +From stable+bounces-274893-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:28 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 07:16:15 -0400 +Subject: PCI: Fix restoring BARs on BAR resize rollback path +To: stable@vger.kernel.org +Cc: "Ilpo Järvinen" , "Simon Richter" , "Alex Bennée" , "Bjorn Helgaas" , "Christian König" , "Sasha Levin" +Message-ID: <20260715111618.647249-3-sashal@kernel.org> + +From: Ilpo Järvinen + +[ 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 +Link: https://lore.kernel.org/linux-pci/f9a8c975-f5d3-4dd2-988e-4371a1433a60@hogyros.de/ +Reported-by: Alex Bennée +Link: https://lore.kernel.org/linux-pci/874irqop6b.fsf@draig.linaro.org/ +Signed-off-by: Ilpo Järvinen +[bhelgaas: squash amdgpu BAR selection from +https: //lore.kernel.org/r/20251114103053.13778-1-ilpo.jarvinen@linux.intel.com] +Signed-off-by: Bjorn Helgaas +Tested-by: Alex Bennée # AVA, AMD GPU +Reviewed-by: Christian König +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 +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 4 - + drivers/pci/pci.h | 3 + drivers/pci/setup-bus.c | 98 +++++++++++++++++++---------- + drivers/pci/setup-res.c | 19 +---- + include/linux/pci.h | 4 - + 5 files changed, 80 insertions(+), 48 deletions(-) + +--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c ++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +@@ -1249,7 +1249,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) + DRM_INFO("Not enough PCI address space for a large BAR."); + else if (r && r != -ENOTSUPP) +--- a/drivers/pci/pci.h ++++ b/drivers/pci/pci.h +@@ -287,6 +287,9 @@ enum pci_bar_type { + struct device *pci_get_host_bridge_device(struct pci_dev *dev); + void pci_put_host_bridge_device(struct device *dev); + ++int pci_do_resource_release_and_resize(struct pci_dev *dev, int resno, int size, ++ int exclude_bars); ++ + int pci_configure_extended_tags(struct pci_dev *dev, void *ign); + bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, + int crs_timeout); +--- a/drivers/pci/setup-bus.c ++++ b/drivers/pci/setup-bus.c +@@ -2166,18 +2166,16 @@ enable_all: + } + EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); + +-int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) ++static int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type, ++ struct list_head *saved) + { + struct pci_dev_resource *dev_res; + struct pci_dev *next; +- LIST_HEAD(saved); + LIST_HEAD(added); + LIST_HEAD(failed); + unsigned int i; + int ret; + +- down_read(&pci_bus_sem); +- + /* Walk to the root hub, releasing bridge BARs when possible */ + next = bridge; + do { +@@ -2193,9 +2191,9 @@ int pci_reassign_bridge_resources(struct + if (res->child) + continue; + +- 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_info(bridge, "BAR %d: releasing %pR\n", + i, res); +@@ -2212,67 +2210,105 @@ int pci_reassign_bridge_resources(struct + next = bridge->bus ? bridge->bus->self : NULL; + } while (next); + +- if (list_empty(&saved)) { +- up_read(&pci_bus_sem); ++ if (list_empty(saved)) + return -ENOENT; +- } + + __pci_bus_size_bridges(bridge->subordinate, &added); + __pci_bridge_assign_resources(bridge, &added, &failed); + BUG_ON(!list_empty(&added)); + + if (!list_empty(&failed)) { +- ret = -ENOSPC; +- goto cleanup; ++ free_list(&failed); ++ return -ENOSPC; + } + +- list_for_each_entry(dev_res, &saved, list) { ++ list_for_each_entry(dev_res, saved, list) { + /* Skip the bridge we just assigned resources for */ + if (bridge == dev_res->dev) + continue; + ++ if (!dev_res->dev->subordinate) ++ continue; ++ + bridge = dev_res->dev; + pci_setup_bridge(bridge->subordinate); + } + +- free_list(&saved); +- up_read(&pci_bus_sem); + return 0; ++} + +-cleanup: +- /* Restore size and flags */ +- list_for_each_entry(dev_res, &failed, list) { +- struct resource *res = dev_res->res; ++int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size, ++ int exclude_bars) ++{ ++ struct resource *res = pdev->resource + resno; ++ unsigned long flags = res->flags; ++ struct pci_dev_resource *dev_res; ++ struct pci_bus *bus = pdev->bus; ++ struct resource *r; ++ LIST_HEAD(saved); ++ unsigned int i; ++ int ret = 0; + +- res->start = dev_res->start; +- res->end = dev_res->end; +- res->flags = dev_res->flags; ++ down_read(&pci_bus_sem); ++ ++ for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { ++ r = pdev->resource + i; ++ ++ if (exclude_bars & BIT(i)) ++ continue; ++ ++ if (!pci_resource_len(pdev, i) || r->flags != flags) ++ continue; ++ ++ ret = add_to_list(&saved, pdev, r, 0, 0); ++ if (ret) ++ goto restore; ++ pci_release_resource(pdev, i); + } +- free_list(&failed); + ++ res->end = res->start + pci_rebar_size_to_bytes(size) - 1; ++ ++ if (!bus->self) ++ goto out; ++ ++ ret = pci_reassign_bridge_resources(bus->self, res->flags, &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; ++ struct pci_dev *dev = dev_res->dev; + +- bridge = dev_res->dev; +- i = res - bridge->resource; ++ i = res - dev->resource; + + if (res->parent) { + release_child_resources(res); +- pci_release_resource(bridge, i); ++ pci_release_resource(dev, i); + } + + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; + +- pci_claim_resource(bridge, i); +- pci_setup_bridge(bridge->subordinate); +- } +- up_read(&pci_bus_sem); +- free_list(&saved); ++ if (pci_claim_resource(dev, i)) ++ continue; + +- return ret; ++ if (i < PCI_BRIDGE_RESOURCES) { ++ pci_update_resource(dev, i); ++ pci_info(dev, "BAR %d %pR: old value restored\n", ++ i, 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 +@@ -422,9 +422,9 @@ void pci_release_resource(struct pci_dev + } + EXPORT_SYMBOL(pci_release_resource); + +-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 = dev->resource + resno; + struct pci_host_bridge *host; + int old, ret; + u32 sizes; +@@ -435,10 +435,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; +- + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_MEMORY) + return -EBUSY; +@@ -458,19 +454,14 @@ int pci_resize_resource(struct pci_dev * + if (ret) + return ret; + +- res->end = res->start + pci_rebar_size_to_bytes(size) - 1; ++ ret = pci_do_resource_release_and_resize(dev, resno, size, exclude_bars); ++ if (ret) ++ goto error_resize; + +- /* Check if the new config works by trying to assign everything. */ +- if (dev->bus->self) { +- ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); +- if (ret) +- goto error_resize; +- } + return 0; + + error_resize: + pci_rebar_set_size(dev, resno, old); +- res->end = res->start + pci_rebar_size_to_bytes(old) - 1; + return ret; + } + EXPORT_SYMBOL(pci_resize_resource); +--- a/include/linux/pci.h ++++ b/include/linux/pci.h +@@ -1317,7 +1317,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); +@@ -1384,7 +1385,6 @@ void pci_assign_unassigned_resources(voi + void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); + void pci_assign_unassigned_bus_resources(struct pci_bus *bus); + void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus); +-int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type); + void pdev_enable_device(struct pci_dev *); + int pci_enable_resources(struct pci_dev *, int mask); + void pci_assign_irq(struct pci_dev *dev); diff --git a/queue-5.15/pci-free-saved-list-without-holding-pci_bus_sem.patch b/queue-5.15/pci-free-saved-list-without-holding-pci_bus_sem.patch new file mode 100644 index 0000000000..700a5fb455 --- /dev/null +++ b/queue-5.15/pci-free-saved-list-without-holding-pci_bus_sem.patch @@ -0,0 +1,38 @@ +From stable+bounces-274892-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:27 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 07:16:14 -0400 +Subject: PCI: Free saved list without holding pci_bus_sem +To: stable@vger.kernel.org +Cc: "Ilpo Järvinen" , "Bjorn Helgaas" , "Alex Bennée" , "Sasha Levin" +Message-ID: <20260715111618.647249-2-sashal@kernel.org> + +From: Ilpo Järvinen + +[ 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 +Signed-off-by: Bjorn Helgaas +Tested-by: Alex Bennée # 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 +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -2269,8 +2269,8 @@ cleanup: + pci_claim_resource(bridge, i); + pci_setup_bridge(bridge->subordinate); + } +- free_list(&saved); + up_read(&pci_bus_sem); ++ free_list(&saved); + + return ret; + } diff --git a/queue-5.15/pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch b/queue-5.15/pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch new file mode 100644 index 0000000000..74e6202c7d --- /dev/null +++ b/queue-5.15/pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch @@ -0,0 +1,55 @@ +From stable+bounces-274537-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:14 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 16:02:20 -0400 +Subject: PCI: imx6: Fix IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling +To: stable@vger.kernel.org +Cc: Richard Zhu , Manivannan Sadhasivam , Bjorn Helgaas , Frank Li , Sasha Levin +Message-ID: <20260714200220.3152632-1-sashal@kernel.org> + +From: Richard Zhu + +[ Upstream commit aad953fb4eed0df5486cd54ccad80ac197678e01 ] + +The IMX6SX_GPR12_PCIE_TEST_POWERDOWN bit does not control the PCIe +reference clock on i.MX6SX. Instead, it is part of i.MX6SX PCIe core +reset sequence. + +Move the IMX6SX_GPR12_PCIE_TEST_POWERDOWN assertion/deassertion into +the core reset functions to properly reflect its purpose. Remove the +.enable_ref_clk() callback for i.MX6SX since it was incorrectly +manipulating this bit. + +Fixes: e3c06cd063d6 ("PCI: imx6: Add initial imx6sx support") +Signed-off-by: Richard Zhu +Signed-off-by: Manivannan Sadhasivam +Signed-off-by: Bjorn Helgaas +Reviewed-by: Frank Li +Cc: stable@vger.kernel.org +Link: https://patch.msgid.link/20260319090844.444987-1-hongxing.zhu@nxp.com +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/controller/dwc/pci-imx6.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/pci/controller/dwc/pci-imx6.c ++++ b/drivers/pci/controller/dwc/pci-imx6.c +@@ -430,9 +430,6 @@ static int imx6_pcie_enable_ref_clk(stru + dev_err(dev, "unable to enable pcie_axi clock\n"); + break; + } +- +- regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, +- IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0); + break; + case IMX6QP: + case IMX6Q: +@@ -559,6 +556,8 @@ static void imx6_pcie_deassert_core_rese + imx7d_pcie_wait_for_phy_pll_lock(imx6_pcie); + break; + case IMX6SX: ++ regmap_clear_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, ++ IMX6SX_GPR12_PCIE_TEST_POWERDOWN); + regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5, + IMX6SX_GPR5_PCIE_BTNRST_RESET, 0); + break; diff --git a/queue-5.15/pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch b/queue-5.15/pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch new file mode 100644 index 0000000000..a33a5319cf --- /dev/null +++ b/queue-5.15/pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch @@ -0,0 +1,127 @@ +From stable+bounces-274635-greg=kroah.com@vger.kernel.org Wed Jul 15 02:18:21 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 20:17:48 -0400 +Subject: PCI: mediatek: Convert bool to single quirks entry and bitmap +To: stable@vger.kernel.org +Cc: Christian Marangi , Manivannan Sadhasivam , AngeloGioacchino Del Regno , Sasha Levin +Message-ID: <20260715001749.3783652-2-sashal@kernel.org> + +From: Christian Marangi + +[ Upstream commit 04305367fab7ec9c98eeba315ad09c8b20abce93 ] + +To clean Mediatek SoC PCIe struct, convert all the bool to a bitmap and +use a single quirks to reference all the values. This permits cleaner +addition of new quirk without having to define a new bool in the struct. + +Signed-off-by: Christian Marangi +Signed-off-by: Manivannan Sadhasivam +Reviewed-by: AngeloGioacchino Del Regno +Link: https://patch.msgid.link/20251020111121.31779-4-ansuelsmth@gmail.com +Stable-dep-of: f865a57896bd ("PCI: mediatek: Fix IRQ domain leak when port fails to enable") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/controller/pcie-mediatek.c | 33 ++++++++++++++++++++------------- + 1 file changed, 20 insertions(+), 13 deletions(-) + +--- a/drivers/pci/controller/pcie-mediatek.c ++++ b/drivers/pci/controller/pcie-mediatek.c +@@ -143,23 +143,31 @@ + struct mtk_pcie_port; + + /** ++ * enum mtk_pcie_quirks - MTK PCIe quirks ++ * @MTK_PCIE_FIX_CLASS_ID: host's class ID needed to be fixed ++ * @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed ++ * @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block ++ */ ++enum mtk_pcie_quirks { ++ MTK_PCIE_FIX_CLASS_ID = BIT(0), ++ MTK_PCIE_FIX_DEVICE_ID = BIT(1), ++ MTK_PCIE_NO_MSI = BIT(2), ++}; ++ ++/** + * struct mtk_pcie_soc - differentiate between host generations +- * @need_fix_class_id: whether this host's class ID needed to be fixed or not +- * @need_fix_device_id: whether this host's device ID needed to be fixed or not +- * @no_msi: Bridge has no MSI support, and relies on an external block + * @device_id: device ID which this host need to be fixed + * @ops: pointer to configuration access functions + * @startup: pointer to controller setting functions + * @setup_irq: pointer to initialize IRQ functions ++ * @quirks: PCIe device quirks. + */ + struct mtk_pcie_soc { +- bool need_fix_class_id; +- bool need_fix_device_id; +- bool no_msi; + unsigned int device_id; + struct pci_ops *ops; + int (*startup)(struct mtk_pcie_port *port); + int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node); ++ enum mtk_pcie_quirks quirks; + }; + + /** +@@ -720,7 +728,7 @@ static int mtk_pcie_startup_port_v2(stru + writel(val, port->base + PCIE_RST_CTRL); + + /* Set up vendor ID and class code */ +- if (soc->need_fix_class_id) { ++ if (soc->quirks & MTK_PCIE_FIX_CLASS_ID) { + val = PCI_VENDOR_ID_MEDIATEK; + writew(val, port->base + PCIE_CONF_VEND_ID); + +@@ -728,7 +736,7 @@ static int mtk_pcie_startup_port_v2(stru + writew(val, port->base + PCIE_CONF_CLASS_ID); + } + +- if (soc->need_fix_device_id) ++ if (soc->quirks & MTK_PCIE_FIX_DEVICE_ID) + writew(soc->device_id, port->base + PCIE_CONF_DEVICE_ID); + + /* 100ms timeout value should be enough for Gen1/2 training */ +@@ -1129,7 +1137,7 @@ static int mtk_pcie_probe(struct platfor + + host->ops = pcie->soc->ops; + host->sysdata = pcie; +- host->msi_domain = pcie->soc->no_msi; ++ host->msi_domain = !!(pcie->soc->quirks & MTK_PCIE_NO_MSI); + + err = pci_host_probe(host); + if (err) +@@ -1219,9 +1227,9 @@ static const struct dev_pm_ops mtk_pcie_ + }; + + static const struct mtk_pcie_soc mtk_pcie_soc_v1 = { +- .no_msi = true, + .ops = &mtk_pcie_ops, + .startup = mtk_pcie_startup_port, ++ .quirks = MTK_PCIE_NO_MSI, + }; + + static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = { +@@ -1231,19 +1239,18 @@ static const struct mtk_pcie_soc mtk_pci + }; + + static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = { +- .need_fix_class_id = true, + .ops = &mtk_pcie_ops_v2, + .startup = mtk_pcie_startup_port_v2, + .setup_irq = mtk_pcie_setup_irq, ++ .quirks = MTK_PCIE_FIX_CLASS_ID, + }; + + static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = { +- .need_fix_class_id = true, +- .need_fix_device_id = true, + .device_id = PCI_DEVICE_ID_MEDIATEK_7629, + .ops = &mtk_pcie_ops_v2, + .startup = mtk_pcie_startup_port_v2, + .setup_irq = mtk_pcie_setup_irq, ++ .quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_FIX_DEVICE_ID, + }; + + static const struct of_device_id mtk_pcie_ids[] = { diff --git a/queue-5.15/pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch b/queue-5.15/pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch new file mode 100644 index 0000000000..11c3d74dd5 --- /dev/null +++ b/queue-5.15/pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch @@ -0,0 +1,170 @@ +From stable+bounces-274636-greg=kroah.com@vger.kernel.org Wed Jul 15 02:18:25 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 20:17:49 -0400 +Subject: PCI: mediatek: Fix IRQ domain leak when port fails to enable +To: stable@vger.kernel.org +Cc: Manivannan Sadhasivam , Manivannan Sadhasivam , Caleb James DeLisle , Sasha Levin +Message-ID: <20260715001749.3783652-3-sashal@kernel.org> + +From: Manivannan Sadhasivam + +[ Upstream commit f865a57896bd92d7662eb2818d8f48872e2cbbc7 ] + +When mtk_pcie_enable_port() fails, mtk_pcie_port_free() removes the port +from pcie->ports and frees the port structure. However, the IRQ domains set +up earlier by mtk_pcie_init_irq_domain() are never freed. + +Fix this by refactoring mtk_pcie_irq_teardown() into a per-port helper, +mtk_pcie_irq_teardown_port(), and calling it from mtk_pcie_setup() when +mtk_pcie_enable_port() fails. Since the IRQ teardown must only happen in +the probe error path (during resume, child devices may have active MSI +mappings and the NOIRQ context prohibits sleeping locks), +mtk_pcie_enable_port() is changed to return an error code so callers can +distinguish the two paths and act accordingly. + +This issue was reported by Sashiko while reviewing the EcoNet EN7528 SoC +support series. + +Fixes: b099631df160 ("PCI: mediatek: Add controller support for MT2712 and MT7622") +Signed-off-by: Manivannan Sadhasivam +Signed-off-by: Manivannan Sadhasivam +Cc: stable@vger.kernel.org # 5.10 +Cc: Caleb James DeLisle +Link: https://patch.msgid.link/20260521174617.17692-1-mani@kernel.org +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/controller/pcie-mediatek.c | 67 ++++++++++++++++++++------------- + 1 file changed, 42 insertions(+), 25 deletions(-) + +--- a/drivers/pci/controller/pcie-mediatek.c ++++ b/drivers/pci/controller/pcie-mediatek.c +@@ -547,25 +547,29 @@ static void mtk_pcie_enable_msi(struct m + writel(val, port->base + PCIE_INT_MASK); + } + +-static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie) ++static void mtk_pcie_irq_teardown_port(struct mtk_pcie_port *port) + { +- struct mtk_pcie_port *port, *tmp; ++ irq_set_chained_handler_and_data(port->irq, NULL, NULL); + +- list_for_each_entry_safe(port, tmp, &pcie->ports, list) { +- irq_set_chained_handler_and_data(port->irq, NULL, NULL); ++ if (port->irq_domain) ++ irq_domain_remove(port->irq_domain); + +- if (port->irq_domain) +- irq_domain_remove(port->irq_domain); ++ if (IS_ENABLED(CONFIG_PCI_MSI)) { ++ if (port->msi_domain) ++ irq_domain_remove(port->msi_domain); ++ if (port->inner_domain) ++ irq_domain_remove(port->inner_domain); ++ } + +- if (IS_ENABLED(CONFIG_PCI_MSI)) { +- if (port->msi_domain) +- irq_domain_remove(port->msi_domain); +- if (port->inner_domain) +- irq_domain_remove(port->inner_domain); +- } ++ irq_dispose_mapping(port->irq); ++} + +- irq_dispose_mapping(port->irq); +- } ++static void mtk_pcie_irq_teardown(struct mtk_pcie *pcie) ++{ ++ struct mtk_pcie_port *port, *tmp; ++ ++ list_for_each_entry_safe(port, tmp, &pcie->ports, list) ++ mtk_pcie_irq_teardown_port(port); + } + + static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq, +@@ -846,7 +850,7 @@ static int mtk_pcie_startup_port(struct + return 0; + } + +-static void mtk_pcie_enable_port(struct mtk_pcie_port *port) ++static int mtk_pcie_enable_port(struct mtk_pcie_port *port) + { + struct mtk_pcie *pcie = port->pcie; + struct device *dev = pcie->dev; +@@ -855,7 +859,7 @@ static void mtk_pcie_enable_port(struct + err = clk_prepare_enable(port->sys_ck); + if (err) { + dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot); +- goto err_sys_clk; ++ return err; + } + + err = clk_prepare_enable(port->ahb_ck); +@@ -903,11 +907,15 @@ static void mtk_pcie_enable_port(struct + goto err_phy_on; + } + +- if (!pcie->soc->startup(port)) +- return; ++ err = pcie->soc->startup(port); ++ if (err) { ++ dev_info(dev, "Port%d link down\n", port->slot); ++ goto err_soc_startup; ++ } + +- dev_info(dev, "Port%d link down\n", port->slot); ++ return 0; + ++err_soc_startup: + phy_power_off(port->phy); + err_phy_on: + phy_exit(port->phy); +@@ -923,8 +931,8 @@ err_aux_clk: + clk_disable_unprepare(port->ahb_ck); + err_ahb_clk: + clk_disable_unprepare(port->sys_ck); +-err_sys_clk: +- mtk_pcie_port_free(port); ++ ++ return err; + } + + static int mtk_pcie_parse_port(struct mtk_pcie *pcie, +@@ -1100,8 +1108,13 @@ static int mtk_pcie_setup(struct mtk_pci + return err; + + /* enable each port, and then check link status */ +- list_for_each_entry_safe(port, tmp, &pcie->ports, list) +- mtk_pcie_enable_port(port); ++ list_for_each_entry_safe(port, tmp, &pcie->ports, list) { ++ err = mtk_pcie_enable_port(port); ++ if (err) { ++ mtk_pcie_irq_teardown_port(port); ++ mtk_pcie_port_free(port); ++ } ++ } + + /* power down PCIe subsys if slots are all empty (link down) */ + if (list_empty(&pcie->ports)) +@@ -1205,14 +1218,18 @@ static int __maybe_unused mtk_pcie_resum + { + struct mtk_pcie *pcie = dev_get_drvdata(dev); + struct mtk_pcie_port *port, *tmp; ++ int err; + + if (list_empty(&pcie->ports)) + return 0; + + clk_prepare_enable(pcie->free_ck); + +- list_for_each_entry_safe(port, tmp, &pcie->ports, list) +- mtk_pcie_enable_port(port); ++ list_for_each_entry_safe(port, tmp, &pcie->ports, list) { ++ err = mtk_pcie_enable_port(port); ++ if (err) ++ mtk_pcie_port_free(port); ++ } + + /* In case of EP was removed while system suspend. */ + if (list_empty(&pcie->ports)) diff --git a/queue-5.15/pci-move-resizable-bar-code-to-rebar.c.patch b/queue-5.15/pci-move-resizable-bar-code-to-rebar.c.patch new file mode 100644 index 0000000000..e47ffbaa75 --- /dev/null +++ b/queue-5.15/pci-move-resizable-bar-code-to-rebar.c.patch @@ -0,0 +1,510 @@ +From stable+bounces-274896-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:44 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 07:16:17 -0400 +Subject: PCI: Move Resizable BAR code to rebar.c +To: stable@vger.kernel.org +Cc: "Ilpo Järvinen" , "Bjorn Helgaas" , "Christian König" , "Sasha Levin" +Message-ID: <20260715111618.647249-5-sashal@kernel.org> + +From: Ilpo Järvinen + +[ 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 +Signed-off-by: Bjorn Helgaas +Reviewed-by: Christian König +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 +Signed-off-by: Greg Kroah-Hartman +--- + Documentation/driver-api/pci/pci.rst | 3 + drivers/pci/Makefile | 2 + drivers/pci/pci.c | 136 ---------------------- + drivers/pci/pci.h | 1 + drivers/pci/rebar.c | 212 +++++++++++++++++++++++++++++++++++ + drivers/pci/setup-res.c | 64 ---------- + 6 files changed, 217 insertions(+), 201 deletions(-) + create mode 100644 drivers/pci/rebar.c + +--- a/Documentation/driver-api/pci/pci.rst ++++ b/Documentation/driver-api/pci/pci.rst +@@ -31,6 +31,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 \ +- pci-sysfs.o rom.o setup-res.o irq.o vpd.o \ ++ pci-sysfs.o rebar.o rom.o setup-res.o irq.o vpd.o \ + setup-bus.o vc.o mmap.o setup-irq.o msi.o + + obj-$(CONFIG_PCI) += pcie/ +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -1663,33 +1663,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 = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); +- if (!pos) +- return; +- +- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); +- nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> +- PCI_REBAR_CTRL_NBAR_SHIFT; +- +- 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 = pdev->resource + bar_idx; +- size = pci_rebar_bytes_to_size(resource_size(res)); +- ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; +- ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT; +- 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 +@@ -3619,115 +3592,6 @@ void pci_acs_init(struct pci_dev *dev) + } + + /** +- * 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; +- +- pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); +- if (!pos) +- return -ENOTSUPP; +- +- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); +- nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> +- PCI_REBAR_CTRL_NBAR_SHIFT; +- +- for (i = 0; i < nbars; i++, pos += 8) { +- int bar_idx; +- +- pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); +- bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; +- 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 19=512GB). 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 (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT; +-} +- +-/** +- * 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, 19=512GB) +- * +- * 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 |= size << PCI_REBAR_CTRL_BAR_SHIFT; +- pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); +- return 0; +-} +- +-/** + * pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port + * @dev: the PCI device + * @cap_mask: mask of desired AtomicOp sizes, including one or more of: +--- a/drivers/pci/pci.h ++++ b/drivers/pci/pci.h +@@ -657,6 +657,7 @@ static inline int acpi_get_rc_resources( + } + #endif + ++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,212 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * PCI Resizable BAR Extended Capability handling. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pci.h" ++ ++/** ++ * 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; ++ ++ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); ++ if (!pos) ++ return -ENOTSUPP; ++ ++ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); ++ nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> ++ PCI_REBAR_CTRL_NBAR_SHIFT; ++ ++ for (i = 0; i < nbars; i++, pos += 8) { ++ int bar_idx; ++ ++ pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); ++ bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; ++ 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 19=512GB). 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 (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT; ++} ++ ++/** ++ * 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, 19=512GB) ++ * ++ * 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 |= size << PCI_REBAR_CTRL_BAR_SHIFT; ++ pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); ++ return 0; ++} ++ ++void pci_restore_rebar_state(struct pci_dev *pdev) ++{ ++ unsigned int pos, nbars, i; ++ u32 ctrl; ++ ++ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); ++ 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 = pdev->resource + bar_idx; ++ size = pci_rebar_bytes_to_size(resource_size(res)); ++ ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; ++ ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT; ++ pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); ++ } ++} ++ ++/** ++ * 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; ++ u16 cmd; ++ ++ /* Check if we must preserve the firmware's resource assignment */ ++ host = pci_find_host_bridge(dev->bus); ++ if (host->preserve_config) ++ return -ENOTSUPP; ++ ++ pci_read_config_word(dev, PCI_COMMAND, &cmd); ++ if (cmd & PCI_COMMAND_MEMORY) ++ 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 +@@ -422,70 +422,6 @@ void pci_release_resource(struct pci_dev + } + EXPORT_SYMBOL(pci_release_resource); + +-/** +- * 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; +- u16 cmd; +- +- /* Check if we must preserve the firmware's resource assignment */ +- host = pci_find_host_bridge(dev->bus); +- if (host->preserve_config) +- return -ENOTSUPP; +- +- pci_read_config_word(dev, PCI_COMMAND, &cmd); +- if (cmd & PCI_COMMAND_MEMORY) +- 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-5.15/pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch b/queue-5.15/pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch new file mode 100644 index 0000000000..f1f7593cab --- /dev/null +++ b/queue-5.15/pci-prevent-resource-tree-corruption-when-bar-resize-fails.patch @@ -0,0 +1,71 @@ +From stable+bounces-274891-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:26 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 07:16:13 -0400 +Subject: PCI: Prevent resource tree corruption when BAR resize fails +To: stable@vger.kernel.org +Cc: "Ilpo Järvinen" , "Simon Richter" , "Alex Bennée" , "Bjorn Helgaas" , "Sasha Levin" +Message-ID: <20260715111618.647249-1-sashal@kernel.org> + +From: Ilpo Järvinen + +[ Upstream commit 91c4c89db41499eea1b29c56655f79c3bae66e93 ] + +pbus_reassign_bridge_resources() saves bridge windows into the saved +list before attempting to adjust resource assignments to perform a BAR +resize operation. If resource adjustments cannot be completed fully, +rollback is attempted by restoring the resource from the saved list. + +The rollback, however, does not check whether the resources it restores were +assigned by the partial resize attempt. If restore changes addresses of the +resource, it can result in corrupting the resource tree. + +An example of a corrupted resource tree with overlapping addresses: + + 6200000000000-6203fbfffffff : pciex@620c3c0000000 + 6200000000000-6203fbff0ffff : PCI Bus 0030:01 + 6200020000000-62000207fffff : 0030:01:00.0 + 6200000000000-6203fbff0ffff : PCI Bus 0030:02 + +A resource that are assigned into the resource tree must remain +unchanged. Thus, release such a resource before attempting to restore +and claim it back. + +For simplicity, always do the release and claim back for the resource +even in the cases where it is restored to the same address range. + +Note: this fix may "break" some cases where devices "worked" because +the resource tree corruption allowed address space double counting to +fit more resource than what can now be assigned without double +counting. The upcoming changes to BAR resizing should address those +scenarios (to the extent possible). + +Fixes: 8bb705e3e79d ("PCI: Add pci_resize_resource() for resizing BARs") +Reported-by: Simon Richter +Link: https://lore.kernel.org/linux-pci/67840a16-99b4-4d8c-9b5c-4721ab0970a2@hogyros.de/ +Reported-by: Alex Bennée +Link: https://lore.kernel.org/linux-pci/874irqop6b.fsf@draig.linaro.org/ +Signed-off-by: Ilpo Järvinen +Signed-off-by: Bjorn Helgaas +Tested-by: Alex Bennée # AVA, AMD GPU +Link: https://patch.msgid.link/20251113162628.5946-2-ilpo.jarvinen@linux.intel.com +Stable-dep-of: ee7471fe968d ("PCI: Skip Resizable BAR restore on read error") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/setup-bus.c | 5 +++++ + 1 file changed, 5 insertions(+) + +--- a/drivers/pci/setup-bus.c ++++ b/drivers/pci/setup-bus.c +@@ -2257,6 +2257,11 @@ cleanup: + bridge = dev_res->dev; + i = res - bridge->resource; + ++ if (res->parent) { ++ release_child_resources(res); ++ pci_release_resource(bridge, i); ++ } ++ + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; diff --git a/queue-5.15/pci-skip-resizable-bar-restore-on-read-error.patch b/queue-5.15/pci-skip-resizable-bar-restore-on-read-error.patch new file mode 100644 index 0000000000..109370602d --- /dev/null +++ b/queue-5.15/pci-skip-resizable-bar-restore-on-read-error.patch @@ -0,0 +1,61 @@ +From stable+bounces-274895-greg=kroah.com@vger.kernel.org Wed Jul 15 13:16:40 2026 +From: Sasha Levin +Date: Wed, 15 Jul 2026 07:16:18 -0400 +Subject: PCI: Skip Resizable BAR restore on read error +To: stable@vger.kernel.org +Cc: Marco Nenciarini , Bjorn Helgaas , Sasha Levin +Message-ID: <20260715111618.647249-6-sashal@kernel.org> + +From: Marco Nenciarini + +[ 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 +Signed-off-by: Bjorn Helgaas +Cc: stable@vger.kernel.org +Link: https://patch.msgid.link/666cac19b5daa0ab0e0ab64454e76b4d24465dbd.1776429882.git.mnencia@kcore.it +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/rebar.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/pci/rebar.c ++++ b/drivers/pci/rebar.c +@@ -131,6 +131,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) { +@@ -138,6 +141,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 = pdev->resource + bar_idx; + size = pci_rebar_bytes_to_size(resource_size(res)); diff --git a/queue-5.15/perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch b/queue-5.15/perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch new file mode 100644 index 0000000000..0323173555 --- /dev/null +++ b/queue-5.15/perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch @@ -0,0 +1,57 @@ +From stable+bounces-276819-greg=kroah.com@vger.kernel.org Thu Jul 16 21:14:38 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:14:28 -0400 +Subject: perf/x86/intel/uncore: Defer ADL global PMON enable to enable_box() +To: stable@vger.kernel.org +Cc: Zide Chen , "Peter Zijlstra (Intel)" , Dapeng Mi , Sasha Levin +Message-ID: <20260716191428.1077510-1-sashal@kernel.org> + +From: Zide Chen + +[ Upstream commit 9a0bb848a37150aeccc10088e141339917d995dc ] + +On some Raptor Cove CPUs, enabling uncore PMON globally at driver init +may increase power consumption even when no perf events are in use. + +Drop adl_uncore_msr_init_box() and defer programming the global control +register to enable_box(), so it is only set when a box is actually used. + +IMC and IMC freerunning counters use a separate control path and are +unaffected. + +Fixes: 772ed05f3c5c ("perf/x86/intel/uncore: Add Alder Lake support") +Signed-off-by: Zide Chen +Signed-off-by: Peter Zijlstra (Intel) +Reviewed-by: Dapeng Mi +Cc: stable@vger.kernel.org +Link: https://patch.msgid.link/20260602144908.263680-5-zide.chen@intel.com +[ deleted adl_uncore_msr_init_box() in its 6.12 wrmsrl() spelling since the tree predates the wrmsrq() rename ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/events/intel/uncore_snb.c | 7 ------- + 1 file changed, 7 deletions(-) + +--- a/arch/x86/events/intel/uncore_snb.c ++++ b/arch/x86/events/intel/uncore_snb.c +@@ -445,12 +445,6 @@ void tgl_uncore_cpu_init(void) + skl_uncore_msr_ops.init_box = rkl_uncore_msr_init_box; + } + +-static void adl_uncore_msr_init_box(struct intel_uncore_box *box) +-{ +- if (box->pmu->pmu_idx == 0) +- wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN); +-} +- + static void adl_uncore_msr_enable_box(struct intel_uncore_box *box) + { + wrmsrl(ADL_UNC_PERF_GLOBAL_CTL, SNB_UNC_GLOBAL_CTL_EN); +@@ -469,7 +463,6 @@ static void adl_uncore_msr_exit_box(stru + } + + static struct intel_uncore_ops adl_uncore_msr_ops = { +- .init_box = adl_uncore_msr_init_box, + .enable_box = adl_uncore_msr_enable_box, + .disable_box = adl_uncore_msr_disable_box, + .exit_box = adl_uncore_msr_exit_box, diff --git a/queue-5.15/proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch b/queue-5.15/proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch new file mode 100644 index 0000000000..756330fc66 --- /dev/null +++ b/queue-5.15/proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch @@ -0,0 +1,163 @@ +From stable+bounces-277055-greg=kroah.com@vger.kernel.org Fri Jul 17 16:03:50 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 10:00:14 -0400 +Subject: proc: Move fdinfo PTRACE_MODE_READ check into the inode .permission operation +To: stable@vger.kernel.org +Cc: "Tyler Hicks (Microsoft)" , "Kevin Parsons (Microsoft)" , "Christian Brauner" , "Christian König" , "Jann Horn" , "Kalesh Singh" , "Hardik Garg" , "Allen Pais" , "Sasha Levin" +Message-ID: <20260717140016.1717138-2-sashal@kernel.org> + +From: "Tyler Hicks (Microsoft)" + +[ Upstream commit 0a960ba49869ebe8ff859d000351504dd6b93b68 ] + +The following commits loosened the permissions of /proc//fdinfo/ +directory, as well as the files within it, from 0500 to 0555 while also +introducing a PTRACE_MODE_READ check between the current task and +'s task: + + - commit 7bc3fa0172a4 ("procfs: allow reading fdinfo with PTRACE_MODE_READ") + - commit 1927e498aee1 ("procfs: prevent unprivileged processes accessing fdinfo dir") + +Before those changes, inode based system calls like inotify_add_watch(2) +would fail when the current task didn't have sufficient read permissions: + + [...] + lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0500, st_size=0, ...}) = 0 + inotify_add_watch(64, "/proc/1/task/1/fdinfo", + IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE| + IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = -1 EACCES (Permission denied) + [...] + +This matches the documented behavior in the inotify_add_watch(2) man +page: + + ERRORS + EACCES Read access to the given file is not permitted. + +After those changes, inotify_add_watch(2) started succeeding despite the +current task not having PTRACE_MODE_READ privileges on the target task: + + [...] + lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0 + inotify_add_watch(64, "/proc/1/task/1/fdinfo", + IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE| + IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = 1757 + openat(AT_FDCWD, "/proc/1/task/1/fdinfo", + O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = -1 EACCES (Permission denied) + [...] + +This change in behavior broke .NET prior to v7. See the github link +below for the v7 commit that inadvertently/quietly (?) fixed .NET after +the kernel changes mentioned above. + +Return to the old behavior by moving the PTRACE_MODE_READ check out of +the file .open operation and into the inode .permission operation: + + [...] + lstat("/proc/1/task/1/fdinfo", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0 + inotify_add_watch(64, "/proc/1/task/1/fdinfo", + IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_CREATE|IN_DELETE| + IN_ONLYDIR|IN_DONT_FOLLOW|IN_EXCL_UNLINK) = -1 EACCES (Permission denied) + [...] + +Reported-by: Kevin Parsons (Microsoft) +Link: https://github.com/dotnet/runtime/commit/89e5469ac591b82d38510fe7de98346cce74ad4f +Link: https://stackoverflow.com/questions/75379065/start-self-contained-net6-build-exe-as-service-on-raspbian-system-unauthorizeda +Fixes: 7bc3fa0172a4 ("procfs: allow reading fdinfo with PTRACE_MODE_READ") +Cc: stable@vger.kernel.org +Cc: Christian Brauner +Cc: Christian König +Cc: Jann Horn +Cc: Kalesh Singh +Cc: Hardik Garg +Cc: Allen Pais +Signed-off-by: Tyler Hicks (Microsoft) +Link: https://lore.kernel.org/r/20240501005646.745089-1-code@tyhicks.com +Signed-off-by: Christian Brauner +Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/proc/fd.c | 42 ++++++++++++++++++++---------------------- + 1 file changed, 20 insertions(+), 22 deletions(-) + +--- a/fs/proc/fd.c ++++ b/fs/proc/fd.c +@@ -72,7 +72,18 @@ out: + return 0; + } + +-static int proc_fdinfo_access_allowed(struct inode *inode) ++static int seq_fdinfo_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, seq_show, inode); ++} ++ ++/** ++ * Shared /proc/pid/fdinfo and /proc/pid/fdinfo/fd permission helper to ensure ++ * that the current task has PTRACE_MODE_READ in addition to the normal ++ * POSIX-like checks. ++ */ ++static int proc_fdinfo_permission(struct mnt_idmap *idmap, struct inode *inode, ++ int mask) + { + bool allowed = false; + struct task_struct *task = get_proc_task(inode); +@@ -86,18 +97,13 @@ static int proc_fdinfo_access_allowed(st + if (!allowed) + return -EACCES; + +- return 0; ++ return generic_permission(idmap, inode, mask); + } + +-static int seq_fdinfo_open(struct inode *inode, struct file *file) +-{ +- int ret = proc_fdinfo_access_allowed(inode); +- +- if (ret) +- return ret; +- +- return single_open(file, seq_show, inode); +-} ++static const struct inode_operations proc_fdinfo_file_inode_operations = { ++ .permission = proc_fdinfo_permission, ++ .setattr = proc_setattr, ++}; + + static const struct file_operations proc_fdinfo_file_operations = { + .open = seq_fdinfo_open, +@@ -339,6 +345,8 @@ static struct dentry *proc_fdinfo_instan + ei = PROC_I(inode); + ei->fd = data->fd; + ++ inode->i_op = &proc_fdinfo_file_inode_operations; ++ + inode->i_fop = &proc_fdinfo_file_operations; + tid_fd_update_inode(task, inode, 0); + +@@ -358,23 +366,13 @@ static int proc_readfdinfo(struct file * + proc_fdinfo_instantiate); + } + +-static int proc_open_fdinfo(struct inode *inode, struct file *file) +-{ +- int ret = proc_fdinfo_access_allowed(inode); +- +- if (ret) +- return ret; +- +- return 0; +-} +- + const struct inode_operations proc_fdinfo_inode_operations = { + .lookup = proc_lookupfdinfo, ++ .permission = proc_fdinfo_permission, + .setattr = proc_setattr, + }; + + const struct file_operations proc_fdinfo_operations = { +- .open = proc_open_fdinfo, + .read = generic_read_dir, + .iterate_shared = proc_readfdinfo, + .llseek = generic_file_llseek, diff --git a/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch b/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch new file mode 100644 index 0000000000..50fb51f3ef --- /dev/null +++ b/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch @@ -0,0 +1,274 @@ +From stable+bounces-277057-greg=kroah.com@vger.kernel.org Fri Jul 17 16:00:26 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 10:00:16 -0400 +Subject: proc: protect ptrace_may_access() with exec_update_lock (FD links) +To: stable@vger.kernel.org +Cc: Jann Horn , "Christian Brauner (Amutable)" , Sasha Levin +Message-ID: <20260717140016.1717138-4-sashal@kernel.org> + +From: Jann Horn + +[ 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//fd/ symlinks") +Cc: stable@vger.kernel.org +Signed-off-by: Jann Horn +Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-2-5c3d20e0ac33@google.com +Signed-off-by: Christian Brauner (Amutable) +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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 int proc_fd_access_allowed(struct inode *inode) +-{ +- struct task_struct *task; +- int allowed = 0; +- /* 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 user_namespace *mnt_userns, struct dentry *dentry, + struct iattr *attr) + { +@@ -1779,16 +1753,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); +@@ -1798,26 +1768,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); + } + +@@ -1851,17 +1837,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; + } + +@@ -2247,21 +2227,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 +@@ -170,24 +170,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 +@@ -97,7 +97,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-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch b/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch new file mode 100644 index 0000000000..507716e20c --- /dev/null +++ b/queue-5.15/proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch @@ -0,0 +1,210 @@ +From stable+bounces-277174-greg=kroah.com@vger.kernel.org Fri Jul 17 20:15:51 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 14:11:49 -0400 +Subject: proc: protect ptrace_may_access() with exec_update_lock (part 1) +To: stable@vger.kernel.org +Cc: Jann Horn , "Christian Brauner (Amutable)" , Sasha Levin +Message-ID: <20260717181149.2086640-1-sashal@kernel.org> + +From: Jann Horn + +[ 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 +Link: https://patch.msgid.link/20260518-procfs-lockfix-part1-v1-1-5c3d20e0ac33@google.com +Signed-off-by: Christian Brauner (Amutable) +[ converted NULL return from mm_access() to ERR_PTR(-ESRCH) at call sites since 5.15's mm_access() returns NULL when the task has no mm ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/proc/array.c | 6 ++++++ + fs/proc/base.c | 41 ++++++++++++++++++++++++----------------- + fs/proc/namespaces.c | 12 ++++++++++++ + 3 files changed, 42 insertions(+), 17 deletions(-) + +--- a/fs/proc/array.c ++++ b/fs/proc/array.c +@@ -468,6 +468,11 @@ static int do_task_stat(struct seq_file + unsigned long flags; + struct signal_struct *sig = task->signal; + unsigned int seq = 1; ++ int ret; ++ ++ ret = down_read_killable(&task->signal->exec_update_lock); ++ if (ret) ++ return ret; + + state = *get_task_state(task); + vsize = eip = esp = 0; +@@ -642,6 +647,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 */ +@@ -2332,17 +2338,17 @@ 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); ++ mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); + if (!mm) ++ mm = ERR_PTR(-ESRCH); ++ if (IS_ERR(mm)) { ++ result = ERR_CAST(mm); + goto out_put_task; ++ } + + result = ERR_PTR(-EINTR); + if (mmap_read_lock_killable(mm)) +@@ -2391,23 +2397,24 @@ 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); ++ mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); + if (!mm) ++ mm = ERR_PTR(-ESRCH); ++ 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; + +@@ -2431,8 +2438,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; +@@ -2440,7 +2446,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 */ +@@ -2457,6 +2462,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); + } ++ up_read(&task->signal->exec_update_lock); ++out_put_task: + put_task_struct(task); + return res; + } diff --git a/queue-5.15/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch b/queue-5.15/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch new file mode 100644 index 0000000000..9a664c47ef --- /dev/null +++ b/queue-5.15/proc-rename-proc_setattr-to-proc_nochmod_setattr.patch @@ -0,0 +1,221 @@ +From stable+bounces-277056-greg=kroah.com@vger.kernel.org Fri Jul 17 16:01:36 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 10:00:15 -0400 +Subject: proc: rename proc_setattr to proc_nochmod_setattr +To: stable@vger.kernel.org +Cc: Christoph Hellwig , Jan Kara , Christian Brauner , Sasha Levin +Message-ID: <20260717140016.1717138-3-sashal@kernel.org> + +From: Christoph Hellwig + +[ 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// 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 +Link: https://patch.msgid.link/20260325063711.3298685-5-hch@lst.de +Reviewed-by: Jan Kara +Signed-off-by: Christian Brauner +Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/proc/base.c | 22 +++++++++++----------- + fs/proc/fd.c | 12 ++++++------ + fs/proc/internal.h | 4 ++-- + fs/proc/namespaces.c | 4 ++-- + fs/proc/proc_net.c | 2 +- + 5 files changed, 22 insertions(+), 22 deletions(-) + +--- a/fs/proc/base.c ++++ b/fs/proc/base.c +@@ -721,7 +721,7 @@ static int proc_fd_access_allowed(struct + return allowed; + } + +-int proc_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ++int proc_nochmod_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, + struct iattr *attr) + { + int error; +@@ -795,7 +795,7 @@ static int proc_pid_permission(struct us + + + 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) +@@ -1868,7 +1868,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, + }; + + +@@ -2316,7 +2316,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 * +@@ -2395,7 +2395,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 +@@ -2887,7 +2887,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 +@@ -2946,7 +2946,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 +@@ -3403,7 +3403,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, + }; + +@@ -3603,7 +3603,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, + }; + +@@ -3730,7 +3730,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, +@@ -3926,7 +3926,7 @@ static int proc_task_getattr(struct user + 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 +@@ -82,8 +82,8 @@ static int seq_fdinfo_open(struct inode + * that the current task has PTRACE_MODE_READ in addition to the normal + * POSIX-like checks. + */ +-static int proc_fdinfo_permission(struct mnt_idmap *idmap, struct inode *inode, +- int mask) ++static int proc_fdinfo_permission(struct user_namespace *mnt_userns, ++ struct inode *inode, int mask) + { + bool allowed = false; + struct task_struct *task = get_proc_task(inode); +@@ -97,12 +97,12 @@ static int proc_fdinfo_permission(struct + if (!allowed) + return -EACCES; + +- return generic_permission(idmap, inode, mask); ++ return generic_permission(mnt_userns, inode, mask); + } + + 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 = { +@@ -328,7 +328,7 @@ int proc_fd_permission(struct user_names + const struct inode_operations proc_fd_inode_operations = { + .lookup = proc_lookupfd, + .permission = proc_fd_permission, +- .setattr = proc_setattr, ++ .setattr = proc_nochmod_setattr, + }; + + static struct dentry *proc_fdinfo_instantiate(struct dentry *dentry, +@@ -369,7 +369,7 @@ static int proc_readfdinfo(struct file * + 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 +@@ -178,8 +178,8 @@ extern int proc_pid_statm(struct seq_fil + extern const struct dentry_operations pid_dentry_operations; + extern int pid_getattr(struct user_namespace *, const struct path *, + struct kstat *, u32, unsigned int); +-extern int proc_setattr(struct user_namespace *, struct dentry *, +- struct iattr *); ++int proc_nochmod_setattr(struct user_namespace *mnt_userns, 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, +@@ -179,5 +179,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 +@@ -311,7 +311,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-5.15/proc-use-generic-setattr-for-proc-pid-net.patch b/queue-5.15/proc-use-generic-setattr-for-proc-pid-net.patch new file mode 100644 index 0000000000..0e03ddac52 --- /dev/null +++ b/queue-5.15/proc-use-generic-setattr-for-proc-pid-net.patch @@ -0,0 +1,46 @@ +From stable+bounces-277054-greg=kroah.com@vger.kernel.org Fri Jul 17 16:00:23 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 10:00:13 -0400 +Subject: proc: use generic setattr() for /proc/$PID/net +To: stable@vger.kernel.org +Cc: "Thomas Weißschuh" , "Zhangjin Wu" , "Willy Tarreau" , "Christian Brauner" , "Sasha Levin" +Message-ID: <20260717140016.1717138-1-sashal@kernel.org> + +From: Thomas Weißschuh + +[ Upstream commit 18e66ae67673f2c8df6f02428798b1355691f2a9 ] + +All other files in /proc/$PID/ use proc_setattr(). + +Not using it allows the usage of chmod() on /proc/$PID/net, even on +other processes owned by the same user. +The same would probably also be true for other attributes to be changed. + +As this technically represents an ABI change it is not marked for +stable so any unlikely regressions are caught during a full release cycle. + +Fixes: e9720acd728a ("[NET]: Make /proc/net a symlink on /proc/self/net (v3)") +Signed-off-by: Thomas Weißschuh +Link: https://lore.kernel.org/lkml/d0d111ef-edae-4760-83fb-36db84278da1@t-8ch.de/ +Fixes: b4844fa0bdb4 ("selftests/nolibc: implement a few tests for various syscalls") +Tested-by: Zhangjin Wu +Signed-off-by: Willy Tarreau +Message-Id: <20230624-proc-net-setattr-v1-2-73176812adee@weissschuh.net> +Signed-off-by: Christian Brauner +Stable-dep-of: 6255da28d4bb ("proc: protect ptrace_may_access() with exec_update_lock (FD links)") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/proc/proc_net.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/proc/proc_net.c ++++ b/fs/proc/proc_net.c +@@ -311,6 +311,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, + }; + + static int proc_tgid_net_readdir(struct file *file, struct dir_context *ctx) diff --git a/queue-5.15/regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch b/queue-5.15/regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch new file mode 100644 index 0000000000..7013f8d45d --- /dev/null +++ b/queue-5.15/regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch @@ -0,0 +1,42 @@ +From stable+bounces-278566-greg=kroah.com@vger.kernel.org Tue Jul 21 13:51:56 2026 +From: Sasha Levin +Date: Tue, 21 Jul 2026 07:47:38 -0400 +Subject: regulator: scmi: fix of_node refcount leak in scmi_regulator_probe() +To: stable@vger.kernel.org +Cc: Wentao Liang , Mark Brown , Sasha Levin +Message-ID: <20260721114738.3689493-1-sashal@kernel.org> + +From: Wentao Liang + +[ Upstream commit fa11039d6cdff84584a3ef8cc1f5e1b56e045da2 ] + +scmi_regulator_probe() calls of_find_node_by_name() which takes a +reference on the returned device node. On the error path where +process_scmi_regulator_of_node() fails, the function returns without +calling of_node_put() on the child node, leaking the reference. + +Add of_node_put(np) on the error path to properly release the +reference. + +Cc: stable@vger.kernel.org +Fixes: 0fbeae70ee7c ("regulator: add SCMI driver") +Signed-off-by: Wentao Liang +Link: https://patch.msgid.link/20260527104850.872415-1-vulab@iscas.ac.cn +Signed-off-by: Mark Brown +[ kept the existing of_node_put(child) since 5.15 still uses the non-scoped for_each_child_of_node() loop, only adding the new of_node_put(np) line ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/regulator/scmi-regulator.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/regulator/scmi-regulator.c ++++ b/drivers/regulator/scmi-regulator.c +@@ -350,6 +350,7 @@ static int scmi_regulator_probe(struct s + /* abort on any mem issue */ + if (ret == -ENOMEM) { + of_node_put(child); ++ of_node_put(np); + return ret; + } + } diff --git a/queue-5.15/serial-8250_mid-disable-dma-for-selected-platforms.patch b/queue-5.15/serial-8250_mid-disable-dma-for-selected-platforms.patch new file mode 100644 index 0000000000..9326ca1883 --- /dev/null +++ b/queue-5.15/serial-8250_mid-disable-dma-for-selected-platforms.patch @@ -0,0 +1,64 @@ +From stable+bounces-277231-greg=kroah.com@vger.kernel.org Sat Jul 18 03:00:13 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 20:59:58 -0400 +Subject: serial: 8250_mid: Disable DMA for selected platforms +To: stable@vger.kernel.org +Cc: Andy Shevchenko , micas-opensource , stable , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260718005958.2505769-2-sashal@kernel.org> + +From: Andy Shevchenko + +[ Upstream commit b1b4efea05a56c0995e4702a86d6624b4fdff32f ] + +In accordance with Errata (specification updates) +HSUART May Stop Functioning when DMA is Active. + +- Denverton document #572409, rev 3.4, DNV60 +- Ice Lake Xeon D document #714070, ICXD65 +- Snowridge document #731931, SNR44 + +For a quick fix just disable the respective callbacks during the device probe. +Depending on the future development we might remove them completely. + +Reported-by: micas-opensource +Closes: https://lore.kernel.org/linux-serial/20250625031409.2404219-1-opensource@ruijie.com.cn/ +Fixes: 6ede6dcd87aa ("serial: 8250_mid: add support for DMA engine handling from UART MMIO") +Cc: stable +Signed-off-by: Andy Shevchenko +Link: https://patch.msgid.link/20260626094937.561776-1-andriy.shevchenko@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/8250/8250_mid.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +--- a/drivers/tty/serial/8250/8250_mid.c ++++ b/drivers/tty/serial/8250/8250_mid.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + + #include + +@@ -371,8 +372,16 @@ static const struct mid8250_board dnv_bo + .freq = 133333333, + .base_baud = 115200, + .bar = 1, +- .setup = dnv_setup, +- .exit = dnv_exit, ++ /* ++ * Errata: ++ * HSUART May Stop Functioning when DMA is Active. ++ * ++ * - Denverton document #572409, rev 3.4, DNV60 ++ * - Ice Lake Xeon D document #714070, ICXD65 ++ * - Snowridge document #731931, SNR44 ++ */ ++ .setup = PTR_IF(false, dnv_setup), ++ .exit = PTR_IF(false, dnv_exit), + }; + + #define MID_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board } diff --git a/queue-5.15/serial-8250_mid-remove-8250_pci-usage.patch b/queue-5.15/serial-8250_mid-remove-8250_pci-usage.patch new file mode 100644 index 0000000000..b433c5c49e --- /dev/null +++ b/queue-5.15/serial-8250_mid-remove-8250_pci-usage.patch @@ -0,0 +1,120 @@ +From stable+bounces-277230-greg=kroah.com@vger.kernel.org Sat Jul 18 03:00:15 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 20:59:57 -0400 +Subject: serial: 8250_mid: Remove 8250_pci usage +To: stable@vger.kernel.org +Cc: "Ilpo Järvinen" , "Greg Kroah-Hartman" , "Sasha Levin" +Message-ID: <20260718005958.2505769-1-sashal@kernel.org> + +From: Ilpo Järvinen + +[ Upstream commit a136abd7e7abe0f1247f8ffde6cc7c8ab09f985b ] + +8250_mid uses FL_*BASE* from linux/8250_pci.h and nothing else. The +code can be simplified by directly defining BARs within the driver +instead. + +Signed-off-by: Ilpo Järvinen +Link: https://lore.kernel.org/r/20230915094336.13278-1-ilpo.jarvinen@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: b1b4efea05a5 ("serial: 8250_mid: Disable DMA for selected platforms") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/8250/8250_mid.c | 18 +++++++----------- + 1 file changed, 7 insertions(+), 11 deletions(-) + +--- a/drivers/tty/serial/8250/8250_mid.c ++++ b/drivers/tty/serial/8250/8250_mid.c +@@ -12,7 +12,6 @@ + #include + + #include +-#include + + #include "8250.h" + +@@ -32,9 +31,9 @@ + struct mid8250; + + struct mid8250_board { +- unsigned int flags; + unsigned long freq; + unsigned int base_baud; ++ unsigned int bar; + int (*setup)(struct mid8250 *, struct uart_port *p); + void (*exit)(struct mid8250 *); + }; +@@ -169,7 +168,6 @@ static int dnv_setup(struct mid8250 *mid + { + struct hsu_dma_chip *chip = &mid->dma_chip; + struct pci_dev *pdev = to_pci_dev(p->dev); +- unsigned int bar = FL_GET_BASE(mid->board->flags); + int ret; + + pci_set_master(pdev); +@@ -183,7 +181,7 @@ static int dnv_setup(struct mid8250 *mid + chip->dev = &pdev->dev; + chip->irq = pci_irq_vector(pdev, 0); + chip->regs = p->membase; +- chip->length = pci_resource_len(pdev, bar); ++ chip->length = pci_resource_len(pdev, mid->board->bar); + chip->offset = DNV_DMA_CHAN_OFFSET; + + /* Falling back to PIO mode if DMA probing fails */ +@@ -292,7 +290,6 @@ static int mid8250_probe(struct pci_dev + { + struct uart_8250_port uart; + struct mid8250 *mid; +- unsigned int bar; + int ret; + + ret = pcim_enable_device(pdev); +@@ -304,7 +301,6 @@ static int mid8250_probe(struct pci_dev + return -ENOMEM; + + mid->board = (struct mid8250_board *)id->driver_data; +- bar = FL_GET_BASE(mid->board->flags); + + memset(&uart, 0, sizeof(struct uart_8250_port)); + +@@ -317,8 +313,8 @@ static int mid8250_probe(struct pci_dev + uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.port.set_termios = mid8250_set_termios; + +- uart.port.mapbase = pci_resource_start(pdev, bar); +- uart.port.membase = pcim_iomap(pdev, bar, 0); ++ uart.port.mapbase = pci_resource_start(pdev, mid->board->bar); ++ uart.port.membase = pcim_iomap(pdev, mid->board->bar, 0); + if (!uart.port.membase) + return -ENOMEM; + +@@ -356,25 +352,25 @@ static void mid8250_remove(struct pci_de + } + + static const struct mid8250_board pnw_board = { +- .flags = FL_BASE0, + .freq = 50000000, + .base_baud = 115200, ++ .bar = 0, + .setup = pnw_setup, + .exit = pnw_exit, + }; + + static const struct mid8250_board tng_board = { +- .flags = FL_BASE0, + .freq = 38400000, + .base_baud = 1843200, ++ .bar = 0, + .setup = tng_setup, + .exit = tng_exit, + }; + + static const struct mid8250_board dnv_board = { +- .flags = FL_BASE1, + .freq = 133333333, + .base_baud = 115200, ++ .bar = 1, + .setup = dnv_setup, + .exit = dnv_exit, + }; diff --git a/queue-5.15/series b/queue-5.15/series index 6aea9df204..7fc3cdb0ce 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -727,3 +727,107 @@ mmc-vub300-defer-reset-until-cmd_mutex-is-unlocked.patch mtd-rawnand-fsl_ifc-return-errors-for-failed-page-reads.patch mtd-rawnand-lpc32xx_mlc-fail-dma-transfers-on-timeout.patch mtd-rawnand-lpc32xx_slc-fail-dma-transfer-on-completion-timeout.patch +acpi-cppc-suppress-ubsan-warning-caused-by-field-misuse.patch +acpi-nfit-core-fix-acpi_nfit_init-error-cleanup.patch +iio-imu-adis-add-irqf_no_thread-to-non-fifo-trigger-irq.patch +iio-hid-sensor-rotation-fix-stale-or-zero-output-when-reading-raw-values.patch +iio-imu-inv_icm42600-make-timestamp-module-chip-independent.patch +iio-move-inv_icm42600-timestamp-module-in-common.patch +iio-make-invensense-timestamp-module-generic.patch +iio-imu-inv_mpu6050-use-the-common-inv_sensors-timestamp-module.patch +iio-invensense-remove-redundant-initialization-of-variable-period.patch +iio-invensense-fix-timestamp-glitches-when-switching-frequency.patch +iio-imu-inv_icm42600-stabilized-timestamp-in-interrupt.patch +iio-imu-inv_icm42600-fix-timestamping-by-limiting-fifo-reading.patch +bitops-make-bytes_to_bits-treewide-available.patch +iio-common-st_sensors-honour-channel-endianness-in-read_axis_data.patch +alsa-aoa-check-snd_ctl_new1-return-value.patch +vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch +pci-altera-fix-resource-leaks-on-probe-failure.patch +cifs-create-a-new-shared-file-holding-smb2-pdu-definitions.patch +cifs-remove-check-of-list-iterator-against-head-past-the-loop-body.patch +smb2-small-refactor-in-smb2_check_message.patch +cifs-remove-unused-server-parameter-from-calc_smb_size.patch +smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch +pci-imx6-fix-imx6sx_gpr12_pcie_test_powerdown-handling.patch +staging-rtl8723bs-fix-indentation-issues.patch +staging-rtl8723bs-fix-space-issues.patch +staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch +pci-controller-use-dev_fwnode-instead-of-of_fwnode_handle.patch +pci-mediatek-convert-bool-to-single-quirks-entry-and-bitmap.patch +pci-mediatek-fix-irq-domain-leak-when-port-fails-to-enable.patch +staging-rtl8723bs-remove-redundant-else-branches.patch +staging-rtl8723bs-remove-redundant-braces-in-if-statements.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 +pci-prevent-resource-tree-corruption-when-bar-resize-fails.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 +coresight-etb10-restore-atomic_t-for-shared-reading-state.patch +gpio-sch-use-new-gpio-line-value-setter-callbacks.patch +gpio-sch-use-raw_spinlock_t-in-the-irq-startup-path.patch +netfilter-ebtables-use-vmalloc_array-to-improve-code.patch +netfilter-ebtables-zero-chainstack-array.patch +bluetooth-l2cap-fix-not-tracking-outstanding-tx-ident.patch +bluetooth-l2cap-fix-deadlock-in-l2cap_conn_del.patch +bluetooth-l2cap-cancel-pending_rx_work-before-taking-conn-lock.patch +smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch +cifs-add-tracing-for-the-cifs_tcon-struct-refcounting.patch +smb-client-resolve-swn-tcon-from-live-registrations.patch +smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch +smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch +smb-client-harden-posix-sid-length-parsing.patch +ksmbd-fix-use-after-free-in-__ksmbd_close_fd-via-durable-scavenger.patch +ksmbd-fix-mechtoken-leak-when-spnego-decode-fails-after-token-alloc.patch +ksmbd-destroy-async_ida-in-ksmbd_conn_free.patch +ksmbd-centralize-ksmbd_conn-final-release-to-plug-transport-leak.patch +ksmbd-track-the-connection-owning-a-byte-range-lock.patch +perf-x86-intel-uncore-defer-adl-global-pmon-enable-to-enable_box.patch +x.509-fix-validation-of-asn.1-certificate-header.patch +proc-use-generic-setattr-for-proc-pid-net.patch +proc-move-fdinfo-ptrace_mode_read-check-into-the-inode-.permission-operation.patch +proc-rename-proc_setattr-to-proc_nochmod_setattr.patch +proc-protect-ptrace_may_access-with-exec_update_lock-fd-links.patch +hid-add-haptics-page-defines.patch +hid-multitouch-fix-out-of-bounds-bit-access-on-mt_io_flags.patch +proc-protect-ptrace_may_access-with-exec_update_lock-part-1.patch +treewide-switch-rename-to-timer_delete.patch +hid-appleir-fix-uaf-on-pending-key_up_timer-in-remove.patch +serial-8250_mid-remove-8250_pci-usage.patch +serial-8250_mid-disable-dma-for-selected-platforms.patch +xfs-remove-redundant-assignment-of-mp.patch +xfs-remove-dead-code.patch +xfs-use-null-daddr-for-unset-first-bad-log-block.patch +hfs-hfsplus-prevent-getting-negative-values-of-offset-length.patch +hfs-hfsplus-fix-u32-overflow-in-check_and_correct_requested_length.patch +xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch +bpf-reject-fragmented-frames-in-devmap.patch +bpf-convert-lpm_trie.c-to-rqspinlock.patch +bpf-consistently-use-bpf_rcu_lock_held-everywhere.patch +bpf-allow-lpm-map-access-from-sleepable-bpf-programs.patch +usb-iowarrior-remove-inherent-race-with-minor-number.patch +drm-i2c-sil164-drop-no-op-remove-function.patch +leds-lm3697-remove-duplicated-error-reporting-in-.remove.patch +leds-lm3601x-improve-error-reporting-for-problems-during-.remove.patch +gpio-pca953x-make-platform-teardown-callback-return-void.patch +usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch +usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch +usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch +usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch +crypto-atmel-sha204a-mark-of-related-data-as-maybe-unused.patch +crypto-atmel-drop-explicit-initialization-of-struct-i2c_device_id-driver_data-to-0.patch +crypto-atmel-sha204a-drop-hwrng-quality-reduction-for-atsha204a.patch +usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch +btrfs-do-not-trim-a-device-which-is-not-writeable.patch +crypto-qat-fix-restarting-state-leak-on-allocation-failure.patch +btrfs-fix-false-io-failure-after-falling-back-to-buffered-write.patch +crypto-qat-validate-rsa-crt-component-lengths.patch +btrfs-fix-incorrect-buffered-io-fallback-for-append-direct-writes.patch +audit-add-audit_log_nf_skb-helper-function.patch +audit-fix-potential-integer-overflow-in-audit_log_n_hex.patch +regulator-scmi-fix-of_node-refcount-leak-in-scmi_regulator_probe.patch diff --git a/queue-5.15/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch b/queue-5.15/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch new file mode 100644 index 0000000000..91e71af56f --- /dev/null +++ b/queue-5.15/smb-client-fix-next-buffer-leak-in-receive_encrypted_standard.patch @@ -0,0 +1,56 @@ +From stable+bounces-275245-greg=kroah.com@vger.kernel.org Thu Jul 16 13:21:12 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 07:20:48 -0400 +Subject: smb: client: Fix next buffer leak in receive_encrypted_standard() +To: stable@vger.kernel.org +Cc: Haoxiang Li , Steve French , Sasha Levin +Message-ID: <20260716112048.1732964-2-sashal@kernel.org> + +From: Haoxiang Li + +[ Upstream commit 1c6267a1d5cf4c73b656f8181b310cbbb3e4767b ] + +receive_encrypted_standard() allocates next_buffer before checking +whether the number of compound PDUs already reached MAX_COMPOUND. If +the limit check fails, the function returns immediately and the newly +allocated next_buffer is not assigned to server->smallbuf/server->bigbuf, +making it leaked. + +Move the MAX_COMPOUND check before allocating next_buffer. + +Fixes: b24df3e30cbf ("cifs: update receive_encrypted_standard to handle compounded responses") +Cc: stable@vger.kernel.org +Signed-off-by: Haoxiang Li +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/smb2ops.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -5211,6 +5211,12 @@ receive_encrypted_standard(struct TCP_Se + one_more: + shdr = (struct smb2_hdr *)buf; + next_cmd = le32_to_cpu(shdr->NextCommand); ++ ++ if (*num_mids >= MAX_COMPOUND) { ++ cifs_server_dbg(VFS, "too many PDUs in compound\n"); ++ return -1; ++ } ++ + if (next_cmd) { + if (WARN_ON_ONCE(next_cmd > pdu_length)) + return -1; +@@ -5234,10 +5240,6 @@ one_more: + mid_entry->resp_buf_size = server->pdu_size; + } + +- if (*num_mids >= MAX_COMPOUND) { +- cifs_server_dbg(VFS, "too many PDUs in compound\n"); +- return -1; +- } + bufs[*num_mids] = buf; + mids[(*num_mids)++] = mid_entry; + diff --git a/queue-5.15/smb-client-harden-posix-sid-length-parsing.patch b/queue-5.15/smb-client-harden-posix-sid-length-parsing.patch new file mode 100644 index 0000000000..964925c659 --- /dev/null +++ b/queue-5.15/smb-client-harden-posix-sid-length-parsing.patch @@ -0,0 +1,44 @@ +From stable+bounces-276769-greg=kroah.com@vger.kernel.org Thu Jul 16 18:43:42 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 12:04:23 -0400 +Subject: smb: client: harden POSIX SID length parsing +To: stable@vger.kernel.org +Cc: Zihan Xi , Yuan Tan , Yifan Wu , Juefei Pu , Xin Liu , Ren Wei , Steve French , Sasha Levin +Message-ID: <20260716160423.620093-1-sashal@kernel.org> + +From: Zihan Xi + +[ Upstream commit 7ad2bcf2441430bb2e918fb3ef9a90d775a6e422 ] + +posix_info_sid_size() reads sid[1] to obtain the subauthority count, +but its existing boundary check still accepts buffers with only one +remaining byte. Require two bytes before reading sid[1] so all client +paths that reuse the helper reject truncated POSIX SIDs safely. + +Fixes: 349e13ad30b4 ("cifs: add smb2 POSIX info level") +Cc: stable@vger.kernel.org +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Assisted-by: Codex:gpt-5.4 +Signed-off-by: Zihan Xi +Signed-off-by: Ren Wei +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/smb2pdu.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/cifs/smb2pdu.c ++++ b/fs/cifs/smb2pdu.c +@@ -4592,7 +4592,7 @@ int posix_info_sid_size(const void *beg, + size_t subauth; + int total; + +- if (beg + 1 > end) ++ if (beg + 2 > end) + return -1; + + subauth = *(u8 *)(beg+1); diff --git a/queue-5.15/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch b/queue-5.15/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch new file mode 100644 index 0000000000..7356ea8207 --- /dev/null +++ b/queue-5.15/smb-client-mask-server-provided-mode-to-07777-in-modefromsid.patch @@ -0,0 +1,38 @@ +From stable+bounces-276767-greg=kroah.com@vger.kernel.org Thu Jul 16 18:04:21 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 12:04:14 -0400 +Subject: smb: client: mask server-provided mode to 07777 in modefromsid +To: stable@vger.kernel.org +Cc: Norbert Manthey , Steve French , Sasha Levin +Message-ID: <20260716160414.619549-1-sashal@kernel.org> + +From: Norbert Manthey + +[ Upstream commit e3d9c7160d483fc8f9e225aafad8ecbbc43f3151 ] + +When modefromsid is active, parse_dacl() applies the server-provided +sub_auth[2] value from the NFS mode SID to cf_mode without masking to +07777. Apply the correct masking, same as in the read path. + +Fixes: e2f8fbfb8d09c ("cifs: get mode bits from special sid on stat") +Signed-off-by: Norbert Manthey +Assisted-by: Kiro:claude-opus-4.6 +Cc: stable@vger.kernel.org +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/cifsacl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/cifs/cifsacl.c ++++ b/fs/cifs/cifsacl.c +@@ -887,7 +887,7 @@ static void parse_dacl(struct cifs_acl * + */ + fattr->cf_mode &= ~07777; + fattr->cf_mode |= +- le32_to_cpu(ppace[i]->sid.sub_auth[2]); ++ le32_to_cpu(ppace[i]->sid.sub_auth[2]) & 07777; + break; + } else { + if (compare_sids(&(ppace[i]->sid), pownersid) == 0) { diff --git a/queue-5.15/smb-client-resolve-swn-tcon-from-live-registrations.patch b/queue-5.15/smb-client-resolve-swn-tcon-from-live-registrations.patch new file mode 100644 index 0000000000..843a4865e7 --- /dev/null +++ b/queue-5.15/smb-client-resolve-swn-tcon-from-live-registrations.patch @@ -0,0 +1,623 @@ +From stable+bounces-276752-greg=kroah.com@vger.kernel.org Thu Jul 16 18:30:56 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 11:36:44 -0400 +Subject: smb: client: resolve SWN tcon from live registrations +To: stable@vger.kernel.org +Cc: Michael Bommarito , Steve French , Sasha Levin +Message-ID: <20260716153644.582614-2-sashal@kernel.org> + +From: Michael Bommarito + +cifs_swn_notify() looks up a witness registration by id under +cifs_swnreg_idr_mutex, drops the mutex, and then uses the registration's +cached tcon pointer. That pointer is not a lifetime reference, and it is +not a stable representative once cifs_get_swn_reg() lets multiple tcons +for the same net/share name share one registration id. + +A same-share second mount can keep the cifs_swn_reg alive after the first +tcon unregisters and is freed. The registration then still points at the +freed first tcon, so taking tc_lock or incrementing tc_count through +swnreg->tcon only moves the use-after-free earlier. Taking tc_lock while +holding cifs_swnreg_idr_mutex also violates the documented CIFS lock +order. + +Fix this by making the registration store only the stable witness +identity: id, net name, share name, and notify flags. When a notify +arrives, copy that identity under cifs_swnreg_idr_mutex, drop the mutex, +then find and pin a live witness tcon that currently matches the net/share +pair under the normal cifs_tcp_ses_lock -> tc_lock order. The notification +path uses that pinned tcon directly and drops the reference when done. + +Registration and unregister messages now use the live tcon passed by the +caller instead of a cached tcon in the registration. The final unregister +send is folded into cifs_swn_unregister() while the registration is still +protected by cifs_swnreg_idr_mutex. This removes the previous +find/drop/reacquire raw-pointer window. The release path only removes the +idr entry and frees the stable identity strings. + +This preserves the intended one-registration/many-tcon behavior: a +registration id represents a net/share pair, and notify handling acts on a +live representative selected at use time. It also preserves CLIENT_MOVE +ordering for the representative tcon because the old-IP unregister is sent +before cifs_swn_register() sends the new-IP register. + +Fixes: fed979a7e082 ("cifs: Set witness notification handler for messages from userspace daemon") +Cc: stable@vger.kernel.org +Signed-off-by: Michael Bommarito +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Steve French +(cherry picked from commit ec457f9afe5ae9538bdcd58fd4cb442b9787e183) + +[ sashal: 5.15 adaptations: cifs_swn_resource_state_changed() keeps the + per-channel GlobalMid_Lock tcpStatus reconnect loop (5.15 lacks + cifs_signal_cifsd_for_reconnect(), v5.17), switched from swnreg->tcon + to the tcon argument; cifs_swn_get_tcon() drops the tc_lock pair and + checks tcon->tidStatus == CifsExiting instead of + tcon->status == TID_EXITING -- 5.15 has no tc_lock, tc_count and + tidStatus are protected by the already-held cifs_tcp_ses_lock; + tcon->tree_name is tcon->treeName in 5.15; trace.h hunks applied to + fs/cifs/trace.h (pre fs/smb/client move) ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/cifs_swn.c | 318 +++++++++++++++++++++++++++++++++++++++++++---------- + fs/cifs/trace.h | 2 + 2 files changed, 262 insertions(+), 58 deletions(-) + +--- a/fs/cifs/cifs_swn.c ++++ b/fs/cifs/cifs_swn.c +@@ -28,10 +28,54 @@ struct cifs_swn_reg { + bool net_name_notify; + bool share_name_notify; + bool ip_notify; ++}; + +- struct cifs_tcon *tcon; ++struct cifs_swn_reg_info { ++ int id; ++ unsigned int ref_count; ++ const char *net_name; ++ const char *share_name; ++ bool net_name_notify; ++ bool share_name_notify; ++ bool ip_notify; + }; + ++static void cifs_swn_snapshot_reg(struct cifs_swn_reg *swnreg, ++ struct cifs_swn_reg_info *info) ++{ ++ info->id = swnreg->id; ++ info->ref_count = kref_read(&swnreg->ref_count); ++ info->net_name = swnreg->net_name; ++ info->share_name = swnreg->share_name; ++ info->net_name_notify = swnreg->net_name_notify; ++ info->share_name_notify = swnreg->share_name_notify; ++ info->ip_notify = swnreg->ip_notify; ++} ++ ++static int cifs_swn_dup_reg(struct cifs_swn_reg *swnreg, ++ struct cifs_swn_reg_info *info) ++{ ++ cifs_swn_snapshot_reg(swnreg, info); ++ ++ info->net_name = kstrdup(swnreg->net_name, GFP_KERNEL); ++ if (!info->net_name) ++ return -ENOMEM; ++ ++ info->share_name = kstrdup(swnreg->share_name, GFP_KERNEL); ++ if (!info->share_name) { ++ kfree(info->net_name); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void cifs_swn_free_reg_info(struct cifs_swn_reg_info *info) ++{ ++ kfree(info->net_name); ++ kfree(info->share_name); ++} ++ + static int cifs_swn_auth_info_krb(struct cifs_tcon *tcon, struct sk_buff *skb) + { + int ret; +@@ -73,7 +117,8 @@ static int cifs_swn_auth_info_ntlm(struc + * The authentication information to connect to the witness service is bundled + * into the message. + */ +-static int cifs_swn_send_register_message(struct cifs_swn_reg *swnreg) ++static int cifs_swn_send_register_message(struct cifs_swn_reg_info *swnreg, ++ struct cifs_tcon *tcon) + { + struct sk_buff *skb; + struct genlmsghdr *hdr; +@@ -111,10 +156,10 @@ static int cifs_swn_send_register_messag + * told to switch to it (client move message). In these cases we unregister from the + * server address and register to the new address when we receive the notification. + */ +- if (swnreg->tcon->ses->server->use_swn_dstaddr) +- addr = &swnreg->tcon->ses->server->swn_dstaddr; ++ if (tcon->ses->server->use_swn_dstaddr) ++ addr = &tcon->ses->server->swn_dstaddr; + else +- addr = &swnreg->tcon->ses->server->dstaddr; ++ addr = &tcon->ses->server->dstaddr; + + ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), addr); + if (ret < 0) +@@ -138,10 +183,10 @@ static int cifs_swn_send_register_messag + goto nlmsg_fail; + } + +- authtype = cifs_select_sectype(swnreg->tcon->ses->server, swnreg->tcon->ses->sectype); ++ authtype = cifs_select_sectype(tcon->ses->server, tcon->ses->sectype); + switch (authtype) { + case Kerberos: +- ret = cifs_swn_auth_info_krb(swnreg->tcon, skb); ++ ret = cifs_swn_auth_info_krb(tcon, skb); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to get kerberos auth info: %d\n", __func__, ret); + goto nlmsg_fail; +@@ -149,7 +194,7 @@ static int cifs_swn_send_register_messag + break; + case NTLMv2: + case RawNTLMSSP: +- ret = cifs_swn_auth_info_ntlm(swnreg->tcon, skb); ++ ret = cifs_swn_auth_info_ntlm(tcon, skb); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to get NTLM auth info: %d\n", __func__, ret); + goto nlmsg_fail; +@@ -179,7 +224,8 @@ fail: + /* + * Sends an uregister message to the userspace daemon based on the registration + */ +-static int cifs_swn_send_unregister_message(struct cifs_swn_reg *swnreg) ++static int cifs_swn_send_unregister_message(struct cifs_swn_reg_info *swnreg, ++ struct cifs_tcon *tcon) + { + struct sk_buff *skb; + struct genlmsghdr *hdr; +@@ -208,7 +254,7 @@ static int cifs_swn_send_unregister_mess + goto nlmsg_fail; + + ret = nla_put(skb, CIFS_GENL_ATTR_SWN_IP, sizeof(struct sockaddr_storage), +- &swnreg->tcon->ses->server->dstaddr); ++ &tcon->ses->server->dstaddr); + if (ret < 0) + goto nlmsg_fail; + +@@ -245,6 +291,84 @@ nlmsg_fail: + } + + /* ++ * Allocation-free mirror of extract_hostname() + extract_sharename() from ++ * fs/cifs/unc.c. Those helpers kmalloc(GFP_KERNEL); this runs under the ++ * cifs_tcp_ses_lock spinlock, so we mirror their parsing in place against ++ * the caller's stable net_name/share_name strings. ++ * Keep in sync with unc.c. ++ */ ++static bool cifs_swn_tcon_matches(struct cifs_tcon *tcon, ++ const char *net_name, ++ const char *share_name) ++{ ++ const char *unc = tcon->treeName; ++ const char *host, *share, *delim; ++ size_t host_len, share_len; ++ ++ if (!tcon->use_witness) ++ return false; ++ ++ /* extract_hostname: require strlen(unc) >= 3 */ ++ if (strnlen(unc, 3) < 3) ++ return false; ++ /* extract_hostname: skip all leading '\' characters */ ++ for (host = unc; *host == '\\'; host++) ++ ; ++ if (!*host) ++ return false; ++ delim = strchr(host, '\\'); ++ if (!delim) ++ return false; ++ host_len = delim - host; ++ if (strlen(net_name) != host_len || ++ strncasecmp(host, net_name, host_len)) ++ return false; ++ ++ /* extract_sharename: start at unc + 2, then first '\' onward */ ++ share = unc + 2; ++ delim = strchr(share, '\\'); ++ if (!delim) ++ return false; ++ share = delim + 1; ++ share_len = strlen(share); ++ ++ return strlen(share_name) == share_len && ++ !strncasecmp(share, share_name, share_len); ++} ++ ++/* ++ * One SWN registration id represents one net/share name pair. Multiple ++ * mounted tcons can therefore share the id. Pick a live representative at ++ * use time instead of caching the first tcon pointer in the registration. ++ */ ++static struct cifs_tcon *cifs_swn_get_tcon(struct cifs_swn_reg_info *swnreg) ++{ ++ struct TCP_Server_Info *server; ++ struct cifs_ses *ses; ++ struct cifs_tcon *tcon; ++ ++ spin_lock(&cifs_tcp_ses_lock); ++ list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { ++ list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { ++ list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { ++ if (tcon->tidStatus == CifsExiting || ++ !cifs_swn_tcon_matches(tcon, swnreg->net_name, ++ swnreg->share_name)) ++ continue; ++ ++tcon->tc_count; ++ trace_smb3_tcon_ref(tcon->debug_id, ++ tcon->tc_count, ++ netfs_trace_tcon_ref_get_swn_notify); ++ spin_unlock(&cifs_tcp_ses_lock); ++ return tcon; ++ } ++ } ++ } ++ spin_unlock(&cifs_tcp_ses_lock); ++ return NULL; ++} ++ ++/* + * Try to find a matching registration for the tcon's server name and share name. + * Calls to this function must be protected by cifs_swnreg_idr_mutex. + * TODO Try to avoid memory allocations +@@ -350,8 +474,6 @@ static struct cifs_swn_reg *cifs_get_swn + reg->net_name_notify = true; + reg->share_name_notify = true; + reg->ip_notify = (tcon->capabilities & SMB2_SHARE_CAP_SCALEOUT); +- +- reg->tcon = tcon; + unlock: + mutex_unlock(&cifs_swnreg_idr_mutex); + +@@ -371,11 +493,6 @@ fail_unlock: + static void cifs_swn_reg_release(struct kref *ref) + { + struct cifs_swn_reg *swnreg = container_of(ref, struct cifs_swn_reg, ref_count); +- int ret; +- +- ret = cifs_swn_send_unregister_message(swnreg); +- if (ret < 0) +- cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", __func__, ret); + + idr_remove(&cifs_swnreg_idr, swnreg->id); + kfree(swnreg->net_name); +@@ -383,33 +500,43 @@ static void cifs_swn_reg_release(struct + kfree(swnreg); + } + +-static void cifs_put_swn_reg(struct cifs_swn_reg *swnreg) ++static void cifs_put_swn_reg_locked(struct cifs_swn_reg *swnreg, ++ struct cifs_tcon *tcon) + { +- mutex_lock(&cifs_swnreg_idr_mutex); ++ if (kref_read(&swnreg->ref_count) == 1) { ++ struct cifs_swn_reg_info swnreg_info; ++ int ret; ++ ++ cifs_swn_snapshot_reg(swnreg, &swnreg_info); ++ ret = cifs_swn_send_unregister_message(&swnreg_info, tcon); ++ if (ret < 0) ++ cifs_dbg(VFS, "%s: Failed to send unregister message: %d\n", ++ __func__, ret); ++ } ++ + kref_put(&swnreg->ref_count, cifs_swn_reg_release); +- mutex_unlock(&cifs_swnreg_idr_mutex); + } + +-static int cifs_swn_resource_state_changed(struct cifs_swn_reg *swnreg, const char *name, int state) ++static int cifs_swn_resource_state_changed(struct cifs_tcon *tcon, const char *name, int state) + { + int i; + + switch (state) { + case CIFS_SWN_RESOURCE_STATE_UNAVAILABLE: + cifs_dbg(FYI, "%s: resource name '%s' become unavailable\n", __func__, name); +- for (i = 0; i < swnreg->tcon->ses->chan_count; i++) { ++ for (i = 0; i < tcon->ses->chan_count; i++) { + spin_lock(&GlobalMid_Lock); +- if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting) +- swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect; ++ if (tcon->ses->chans[i].server->tcpStatus != CifsExiting) ++ tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect; + spin_unlock(&GlobalMid_Lock); + } + break; + case CIFS_SWN_RESOURCE_STATE_AVAILABLE: + cifs_dbg(FYI, "%s: resource name '%s' become available\n", __func__, name); +- for (i = 0; i < swnreg->tcon->ses->chan_count; i++) { ++ for (i = 0; i < tcon->ses->chan_count; i++) { + spin_lock(&GlobalMid_Lock); +- if (swnreg->tcon->ses->chans[i].server->tcpStatus != CifsExiting) +- swnreg->tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect; ++ if (tcon->ses->chans[i].server->tcpStatus != CifsExiting) ++ tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect; + spin_unlock(&GlobalMid_Lock); + } + break; +@@ -520,7 +647,7 @@ unlock: + return ret; + } + +-static int cifs_swn_client_move(struct cifs_swn_reg *swnreg, struct sockaddr_storage *addr) ++static int cifs_swn_client_move(struct cifs_tcon *tcon, struct sockaddr_storage *addr) + { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)addr; + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)addr; +@@ -530,14 +657,17 @@ static int cifs_swn_client_move(struct c + else if (addr->ss_family == AF_INET6) + cifs_dbg(FYI, "%s: move to %pI6\n", __func__, &ipv6->sin6_addr); + +- return cifs_swn_reconnect(swnreg->tcon, addr); ++ return cifs_swn_reconnect(tcon, addr); + } + + int cifs_swn_notify(struct sk_buff *skb, struct genl_info *info) + { + struct cifs_swn_reg *swnreg; ++ struct cifs_swn_reg_info swnreg_info; ++ struct cifs_tcon *tcon; + char name[256]; + int type; ++ int ret = 0; + + if (info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]) { + int swnreg_id; +@@ -545,21 +675,34 @@ int cifs_swn_notify(struct sk_buff *skb, + swnreg_id = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_REGISTRATION_ID]); + mutex_lock(&cifs_swnreg_idr_mutex); + swnreg = idr_find(&cifs_swnreg_idr, swnreg_id); +- mutex_unlock(&cifs_swnreg_idr_mutex); + if (swnreg == NULL) { ++ mutex_unlock(&cifs_swnreg_idr_mutex); + cifs_dbg(FYI, "%s: registration id %d not found\n", __func__, swnreg_id); + return -EINVAL; + } ++ ret = cifs_swn_dup_reg(swnreg, &swnreg_info); ++ mutex_unlock(&cifs_swnreg_idr_mutex); ++ if (ret) ++ return ret; + } else { + cifs_dbg(FYI, "%s: missing registration id attribute\n", __func__); + return -EINVAL; + } + ++ tcon = cifs_swn_get_tcon(&swnreg_info); ++ if (!tcon) { ++ cifs_dbg(FYI, "%s: registration id %d has no live tcon\n", ++ __func__, swnreg_info.id); ++ ret = -ENODEV; ++ goto free_info; ++ } ++ + if (info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]) { + type = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_NOTIFICATION_TYPE]); + } else { + cifs_dbg(FYI, "%s: missing notification type attribute\n", __func__); +- return -EINVAL; ++ ret = -EINVAL; ++ goto out; + } + + switch (type) { +@@ -571,15 +714,18 @@ int cifs_swn_notify(struct sk_buff *skb, + sizeof(name)); + } else { + cifs_dbg(FYI, "%s: missing resource name attribute\n", __func__); +- return -EINVAL; ++ ret = -EINVAL; ++ goto out; + } + if (info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]) { + state = nla_get_u32(info->attrs[CIFS_GENL_ATTR_SWN_RESOURCE_STATE]); + } else { + cifs_dbg(FYI, "%s: missing resource state attribute\n", __func__); +- return -EINVAL; ++ ret = -EINVAL; ++ goto out; + } +- return cifs_swn_resource_state_changed(swnreg, name, state); ++ ret = cifs_swn_resource_state_changed(tcon, name, state); ++ break; + } + case CIFS_SWN_NOTIFICATION_CLIENT_MOVE: { + struct sockaddr_storage addr; +@@ -588,28 +734,36 @@ int cifs_swn_notify(struct sk_buff *skb, + nla_memcpy(&addr, info->attrs[CIFS_GENL_ATTR_SWN_IP], sizeof(addr)); + } else { + cifs_dbg(FYI, "%s: missing IP address attribute\n", __func__); +- return -EINVAL; ++ ret = -EINVAL; ++ goto out; + } +- return cifs_swn_client_move(swnreg, &addr); ++ ret = cifs_swn_client_move(tcon, &addr); ++ break; + } + default: + cifs_dbg(FYI, "%s: unknown notification type %d\n", __func__, type); + break; + } + +- return 0; ++out: ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); ++free_info: ++ cifs_swn_free_reg_info(&swnreg_info); ++ return ret; + } + + int cifs_swn_register(struct cifs_tcon *tcon) + { + struct cifs_swn_reg *swnreg; ++ struct cifs_swn_reg_info swnreg_info; + int ret; + + swnreg = cifs_get_swn_reg(tcon); + if (IS_ERR(swnreg)) + return PTR_ERR(swnreg); + +- ret = cifs_swn_send_register_message(swnreg); ++ cifs_swn_snapshot_reg(swnreg, &swnreg_info); ++ ret = cifs_swn_send_register_message(&swnreg_info, tcon); + if (ret < 0) { + cifs_dbg(VFS, "%s: Failed to send swn register message: %d\n", __func__, ret); + /* Do not put the swnreg or return error, the echo task will retry */ +@@ -630,35 +784,68 @@ int cifs_swn_unregister(struct cifs_tcon + return PTR_ERR(swnreg); + } + ++ cifs_put_swn_reg_locked(swnreg, tcon); + mutex_unlock(&cifs_swnreg_idr_mutex); + +- cifs_put_swn_reg(swnreg); +- + return 0; + } + +-void cifs_swn_dump(struct seq_file *m) ++/* ++ * Snapshot one registration under cifs_swnreg_idr_mutex and return. Callers ++ * intentionally do the per-registration network/genlmsg work without the ++ * mutex held, both to keep the critical section short and to avoid nesting ++ * cifs_swnreg_idr_mutex inside the higher cifs_tcp_ses_lock when a live ++ * tcon is then pinned for the send. ++ */ ++static int cifs_swn_get_next_reg_info(int *id, struct cifs_swn_reg_info *info) + { + struct cifs_swn_reg *swnreg; ++ int ret = 0; ++ ++ mutex_lock(&cifs_swnreg_idr_mutex); ++ swnreg = idr_get_next(&cifs_swnreg_idr, id); ++ if (swnreg) { ++ ret = cifs_swn_dup_reg(swnreg, info); ++ if (!ret) { ++ *id = swnreg->id + 1; ++ ret = 1; ++ } ++ } ++ mutex_unlock(&cifs_swnreg_idr_mutex); ++ ++ return ret; ++} ++ ++void cifs_swn_dump(struct seq_file *m) ++{ ++ struct cifs_swn_reg_info swnreg_info; ++ struct cifs_tcon *tcon; + struct sockaddr_in *sa; + struct sockaddr_in6 *sa6; +- int id; ++ int id = 0; ++ int ret; + + seq_puts(m, "Witness registrations:"); + +- mutex_lock(&cifs_swnreg_idr_mutex); +- idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { ++ while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) { + seq_printf(m, "\nId: %u Refs: %u Network name: '%s'%s Share name: '%s'%s Ip address: ", +- id, kref_read(&swnreg->ref_count), +- swnreg->net_name, swnreg->net_name_notify ? "(y)" : "(n)", +- swnreg->share_name, swnreg->share_name_notify ? "(y)" : "(n)"); +- switch (swnreg->tcon->ses->server->dstaddr.ss_family) { ++ swnreg_info.id, swnreg_info.ref_count, ++ swnreg_info.net_name, swnreg_info.net_name_notify ? "(y)" : "(n)", ++ swnreg_info.share_name, swnreg_info.share_name_notify ? "(y)" : "(n)"); ++ ++ tcon = cifs_swn_get_tcon(&swnreg_info); ++ if (!tcon) { ++ seq_puts(m, "(no live tcon)"); ++ goto next; ++ } ++ ++ switch (tcon->ses->server->dstaddr.ss_family) { + case AF_INET: +- sa = (struct sockaddr_in *) &swnreg->tcon->ses->server->dstaddr; ++ sa = (struct sockaddr_in *)&tcon->ses->server->dstaddr; + seq_printf(m, "%pI4", &sa->sin_addr.s_addr); + break; + case AF_INET6: +- sa6 = (struct sockaddr_in6 *) &swnreg->tcon->ses->server->dstaddr; ++ sa6 = (struct sockaddr_in6 *)&tcon->ses->server->dstaddr; + seq_printf(m, "%pI6", &sa6->sin6_addr.s6_addr); + if (sa6->sin6_scope_id) + seq_printf(m, "%%%u", sa6->sin6_scope_id); +@@ -666,23 +853,38 @@ void cifs_swn_dump(struct seq_file *m) + default: + seq_puts(m, "(unknown)"); + } +- seq_printf(m, "%s", swnreg->ip_notify ? "(y)" : "(n)"); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); ++next: ++ seq_printf(m, "%s", swnreg_info.ip_notify ? "(y)" : "(n)"); ++ cifs_swn_free_reg_info(&swnreg_info); + } +- mutex_unlock(&cifs_swnreg_idr_mutex); ++ if (ret < 0) ++ seq_printf(m, "\nFailed to snapshot witness registration: %d", ret); + seq_puts(m, "\n"); + } + + void cifs_swn_check(void) + { +- struct cifs_swn_reg *swnreg; +- int id; ++ struct cifs_swn_reg_info swnreg_info; ++ struct cifs_tcon *tcon; ++ int id = 0; + int ret; + +- mutex_lock(&cifs_swnreg_idr_mutex); +- idr_for_each_entry(&cifs_swnreg_idr, swnreg, id) { +- ret = cifs_swn_send_register_message(swnreg); ++ while ((ret = cifs_swn_get_next_reg_info(&id, &swnreg_info)) > 0) { ++ tcon = cifs_swn_get_tcon(&swnreg_info); ++ if (!tcon) { ++ cifs_dbg(FYI, "%s: registration id %d has no live tcon\n", ++ __func__, swnreg_info.id); ++ goto free_info; ++ } ++ ++ ret = cifs_swn_send_register_message(&swnreg_info, tcon); + if (ret < 0) + cifs_dbg(FYI, "%s: Failed to send register message: %d\n", __func__, ret); ++ cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_swn_notify); ++free_info: ++ cifs_swn_free_reg_info(&swnreg_info); + } +- mutex_unlock(&cifs_swnreg_idr_mutex); ++ if (ret < 0) ++ cifs_dbg(FYI, "%s: Failed to snapshot registration: %d\n", __func__, ret); + } +--- a/fs/cifs/trace.h ++++ b/fs/cifs/trace.h +@@ -30,6 +30,7 @@ + EM(netfs_trace_tcon_ref_get_find, "GET Find ") \ + EM(netfs_trace_tcon_ref_get_find_sess_tcon, "GET FndSes") \ + EM(netfs_trace_tcon_ref_get_reconnect_server, "GET Reconn") \ ++ EM(netfs_trace_tcon_ref_get_swn_notify, "GET SwnNot") \ + EM(netfs_trace_tcon_ref_new, "NEW ") \ + EM(netfs_trace_tcon_ref_new_ipc, "NEW Ipc ") \ + EM(netfs_trace_tcon_ref_new_reconnect_server, "NEW Reconn") \ +@@ -39,6 +40,7 @@ + EM(netfs_trace_tcon_ref_put_mnt_ctx, "PUT MntCtx") \ + EM(netfs_trace_tcon_ref_put_dfs_refer, "PUT DfsRfr") \ + EM(netfs_trace_tcon_ref_put_reconnect_server, "PUT Reconn") \ ++ EM(netfs_trace_tcon_ref_put_swn_notify, "PUT SwnNot") \ + EM(netfs_trace_tcon_ref_put_tlink, "PUT Tlink ") \ + EM(netfs_trace_tcon_ref_see_cancelled_close, "SEE Cn-Cls") \ + EM(netfs_trace_tcon_ref_see_fscache_collision, "SEE FV-CO!") \ diff --git a/queue-5.15/smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch b/queue-5.15/smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch new file mode 100644 index 0000000000..e772565708 --- /dev/null +++ b/queue-5.15/smb-client-restrict-implied-bcc-exemption-to-responses-without-data-area.patch @@ -0,0 +1,206 @@ +From stable+bounces-274538-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:20 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 16:02:15 -0400 +Subject: smb: client: restrict implied bcc[0] exemption to responses without data area +To: stable@vger.kernel.org +Cc: Shoichiro Miyamoto , Steve French , Sasha Levin +Message-ID: <20260714200215.3152449-5-sashal@kernel.org> + +From: Shoichiro Miyamoto + +[ Upstream commit 53b7c271f06be4dd5cfc8c6ef552a8355c891a7f ] + +smb2_check_message() has a long-standing quirk that accepts a response +whose calculated length is one byte larger than the bytes actually +received ("server can return one byte more due to implied bcc[0]"). +This was introduced to accommodate servers that omit the trailing bcc[0] +overlap byte when no data area is present. + +However, the exemption is applied unconditionally, regardless of whether +the command actually carries a data area (has_smb2_data_area[]). When a +response with a data area is subject to the +1 exemption, the reported +data can extend one byte beyond the bytes actually received, yet +smb2_check_message() still accepts it. The subsequent decoder then reads +past the end of the receive buffer. This is reachable during NEGOTIATE +and SESSION_SETUP, before the session is established. + +The resulting out-of-bounds reads are visible under KASAN when mounting +against a non-conforming server; both the SPNEGO/negTokenInit and the +NTLMSSP challenge decoders are affected: + + BUG: KASAN: slab-out-of-bounds in asn1_ber_decoder+0x16a7/0x1b00 + Read of size 1 at addr ffff8880084d67c0 by task mount.cifs/81 + CPU: 1 UID: 0 PID: 81 Comm: mount.cifs Not tainted 7.1.0-rc6 #1 + Call Trace: + + dump_stack_lvl+0x4e/0x70 + print_report+0x157/0x4c9 + kasan_report+0xce/0x100 + asn1_ber_decoder+0x16a7/0x1b00 + decode_negTokenInit+0x19/0x30 + SMB2_negotiate+0x31d9/0x4c90 + cifs_negotiate_protocol+0x1f2/0x3f0 + cifs_get_smb_ses+0x93f/0x17e0 + cifs_mount_get_session+0x7f/0x3a0 + cifs_mount+0xb4/0xcf0 + cifs_smb3_do_mount+0x23a/0x1500 + smb3_get_tree+0x3b0/0x630 + vfs_get_tree+0x82/0x2d0 + fc_mount+0x10/0x1b0 + path_mount+0x50d/0x1de0 + __x64_sys_mount+0x20b/0x270 + do_syscall_64+0xee/0x590 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + + Allocated by task 85: + kmem_cache_alloc_noprof+0x106/0x380 + mempool_alloc_noprof+0x116/0x1e0 + cifs_small_buf_get+0x31/0x80 + allocate_buffers+0x10d/0x2b0 + cifs_demultiplex_thread+0x1d5/0x1d50 + kthread+0x2c6/0x390 + ret_from_fork+0x36e/0x5a0 + ret_from_fork_asm+0x1a/0x30 + The buggy address is located 0 bytes to the right of + allocated 448-byte region [ffff8880084d6600, ffff8880084d67c0) + which belongs to the cache cifs_small_rq of size 448 + + BUG: KASAN: slab-out-of-bounds in kmemdup_noprof+0x36/0x50 + Read of size 329 at addr ffff88800726c678 by task mount.cifs/89 + CPU: 0 UID: 0 PID: 89 Comm: mount.cifs Tainted: G B 7.1.0-rc6 #1 + Call Trace: + + dump_stack_lvl+0x4e/0x70 + print_report+0x157/0x4c9 + kasan_report+0xce/0x100 + kasan_check_range+0x10f/0x1e0 + __asan_memcpy+0x23/0x60 + kmemdup_noprof+0x36/0x50 + decode_ntlmssp_challenge+0x457/0x680 + SMB2_sess_auth_rawntlmssp_negotiate+0x6f0/0xcb0 + SMB2_sess_setup+0x219/0x4f0 + cifs_setup_session+0x248/0xaf0 + cifs_get_smb_ses+0xf79/0x17e0 + cifs_mount_get_session+0x7f/0x3a0 + cifs_mount+0xb4/0xcf0 + cifs_smb3_do_mount+0x23a/0x1500 + smb3_get_tree+0x3b0/0x630 + vfs_get_tree+0x82/0x2d0 + fc_mount+0x10/0x1b0 + path_mount+0x50d/0x1de0 + __x64_sys_mount+0x20b/0x270 + do_syscall_64+0xee/0x590 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + + Allocated by task 93: + kmem_cache_alloc_noprof+0x106/0x380 + mempool_alloc_noprof+0x116/0x1e0 + cifs_small_buf_get+0x31/0x80 + allocate_buffers+0x10d/0x2b0 + cifs_demultiplex_thread+0x1d5/0x1d50 + kthread+0x2c6/0x390 + ret_from_fork+0x36e/0x5a0 + ret_from_fork_asm+0x1a/0x30 + The buggy address is located 120 bytes inside of + allocated 448-byte region [ffff88800726c600, ffff88800726c7c0) + which belongs to the cache cifs_small_rq of size 448 + +Restrict the +1 exemption to responses that have no data area, so that +it still covers the bcc[0] omission it was meant for. When a data area +is present, the +1 discrepancy instead means the reported data length +overruns the received buffer, so the response must be rejected. + +Fixes: 093b2bdad322 ("CIFS: Make demultiplex_thread work with SMB2 code") +Cc: stable@vger.kernel.org +Signed-off-by: Shoichiro Miyamoto +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/smb2misc.c | 32 ++++++++++++++++++++++++++------ + 1 file changed, 26 insertions(+), 6 deletions(-) + +--- a/fs/cifs/smb2misc.c ++++ b/fs/cifs/smb2misc.c +@@ -17,6 +17,8 @@ + #include "smb2glob.h" + #include "nterr.h" + ++static unsigned int __smb2_calc_size(void *buf, bool *have_data); ++ + static int + check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid) + { +@@ -141,6 +143,7 @@ smb2_check_message(char *buf, unsigned i + int command; + __u32 calc_len; /* calculated length */ + __u64 mid; ++ bool have_data; + + /* + * Add function to do table lookup of StructureSize by command +@@ -221,7 +224,8 @@ smb2_check_message(char *buf, unsigned i + } + } + +- calc_len = smb2_calc_size(buf); ++ have_data = false; ++ calc_len = __smb2_calc_size(buf, &have_data); + + /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might + * be 0, and not a real miscalculation */ +@@ -239,8 +243,13 @@ smb2_check_message(char *buf, unsigned i + /* Windows 7 server returns 24 bytes more */ + if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) + return 0; +- /* server can return one byte more due to implied bcc[0] */ +- if (calc_len == len + 1) ++ /* ++ * Server can return one byte more due to implied bcc[0]. ++ * Allow it only when there is no data area; if data_length > 0 ++ * the +1 gap indicates an overreported data length rather than ++ * the bcc[0] omission. ++ */ ++ if (calc_len == len + 1 && !have_data) + return 0; + + /* +@@ -401,14 +410,17 @@ smb2_get_data_area_len(int *off, int *le + /* + * Calculate the size of the SMB message based on the fixed header + * portion, the number of word parameters and the data portion of the message. ++ * If have_data is non-NULL, it is set to true when a non-empty data area was ++ * found (data_length > 0), allowing callers to distinguish the implied bcc[0] ++ * case (no data area) from an overreported data length. + */ +-unsigned int +-smb2_calc_size(void *buf) ++static unsigned int ++__smb2_calc_size(void *buf, bool *have_data) + { + struct smb2_pdu *pdu = (struct smb2_pdu *)buf; + struct smb2_hdr *shdr = &pdu->hdr; + int offset; /* the offset from the beginning of SMB to data area */ +- int data_length; /* the length of the variable length data area */ ++ int data_length = 0; /* the length of the variable length data area */ + /* Structure Size has already been checked to make sure it is 64 */ + int len = le16_to_cpu(shdr->StructureSize); + +@@ -441,9 +453,17 @@ smb2_calc_size(void *buf) + } + calc_size_exit: + cifs_dbg(FYI, "SMB2 len %d\n", len); ++ if (have_data) ++ *have_data = (data_length > 0); + return len; + } + ++unsigned int ++smb2_calc_size(void *buf) ++{ ++ return __smb2_calc_size(buf, NULL); ++} ++ + /* Note: caller must free return buffer */ + __le16 * + cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb) diff --git a/queue-5.15/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch b/queue-5.15/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch new file mode 100644 index 0000000000..bddaa7cd6f --- /dev/null +++ b/queue-5.15/smb-client-use-unaligned-reads-in-parse_posix_ctxt.patch @@ -0,0 +1,42 @@ +From stable+bounces-276768-greg=kroah.com@vger.kernel.org Thu Jul 16 18:43:50 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 12:04:19 -0400 +Subject: smb: client: use unaligned reads in parse_posix_ctxt() +To: stable@vger.kernel.org +Cc: Zihan Xi , Ren Wei , Steve French , Sasha Levin +Message-ID: <20260716160419.619801-1-sashal@kernel.org> + +From: Zihan Xi + +[ Upstream commit b86467cd2691192ad4809a5a6e922fc24b8e9839 ] + +The server controls create-context DataOffset, so the POSIX context data +pointer may be misaligned on strict-alignment architectures. Use +get_unaligned_le32() when reading nlink, reparse_tag, and mode. + +Fixes: 69dda3059e7a ("cifs: add SMB2_open() arg to return POSIX data") +Cc: stable@vger.kernel.org +Signed-off-by: Zihan Xi +Signed-off-by: Ren Wei +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/smb2pdu.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/fs/cifs/smb2pdu.c ++++ b/fs/cifs/smb2pdu.c +@@ -2037,9 +2037,9 @@ parse_posix_ctxt(struct create_context * + + memset(posix, 0, sizeof(*posix)); + +- posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0)); +- posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4)); +- posix->mode = le32_to_cpu(*(__le32 *)(beg + 8)); ++ posix->nlink = get_unaligned_le32(beg); ++ posix->reparse_tag = get_unaligned_le32(beg + 4); ++ posix->mode = get_unaligned_le32(beg + 8); + + sid = beg + 12; + sid_len = posix_info_sid_size(sid, end); diff --git a/queue-5.15/smb2-small-refactor-in-smb2_check_message.patch b/queue-5.15/smb2-small-refactor-in-smb2_check_message.patch new file mode 100644 index 0000000000..9d6de47980 --- /dev/null +++ b/queue-5.15/smb2-small-refactor-in-smb2_check_message.patch @@ -0,0 +1,168 @@ +From stable+bounces-274534-greg=kroah.com@vger.kernel.org Tue Jul 14 22:03:09 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 16:02:13 -0400 +Subject: smb2: small refactor in smb2_check_message() +To: stable@vger.kernel.org +Cc: Enzo Matsumiya , Steve French , Sasha Levin +Message-ID: <20260714200215.3152449-3-sashal@kernel.org> + +From: Enzo Matsumiya + +[ Upstream commit da3847894fddc27ca95d5ac0012f444a77a5e0c3 ] + +If the command is SMB2_IOCTL, OutputLength and OutputContext are +optional and can be zero, so return early and skip calculated length +check. + +Move the mismatched length message to the end of the check, to avoid +unnecessary logs when the check was not a real miscalculation. + +Also change the pr_warn_once() to a pr_warn() so we're sure to get a +log for the real mismatches. + +Signed-off-by: Enzo Matsumiya +Signed-off-by: Steve French +Stable-dep-of: 53b7c271f06b ("smb: client: restrict implied bcc[0] exemption to responses without data area") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/connect.c | 13 ++++++------- + fs/cifs/smb2misc.c | 47 ++++++++++++++++++++++++++++------------------- + 2 files changed, 34 insertions(+), 26 deletions(-) + +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -890,19 +890,18 @@ int + cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) + { + char *buf = server->large_buf ? server->bigbuf : server->smallbuf; +- int length; ++ int rc; + + /* + * We know that we received enough to get to the MID as we + * checked the pdu_length earlier. Now check to see +- * if the rest of the header is OK. We borrow the length +- * var for the rest of the loop to avoid a new stack var. ++ * if the rest of the header is OK. + * + * 48 bytes is enough to display the header and a little bit + * into the payload for debugging purposes. + */ +- length = server->ops->check_message(buf, server->total_read, server); +- if (length != 0) ++ rc = server->ops->check_message(buf, server->total_read, server); ++ if (rc) + cifs_dump_mem("Bad SMB: ", buf, + min_t(unsigned int, server->total_read, 48)); + +@@ -917,9 +916,9 @@ cifs_handle_standard(struct TCP_Server_I + return -1; + + if (!mid) +- return length; ++ return rc; + +- handle_mid(mid, server, buf, length); ++ handle_mid(mid, server, buf, rc); + return 0; + } + +--- a/fs/cifs/smb2misc.c ++++ b/fs/cifs/smb2misc.c +@@ -132,15 +132,15 @@ static __u32 get_neg_ctxt_len(struct smb + } + + int +-smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *srvr) ++smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server) + { + struct smb2_hdr *shdr = (struct smb2_hdr *)buf; + struct smb2_pdu *pdu = (struct smb2_pdu *)shdr; +- __u64 mid; +- __u32 clc_len; /* calculated length */ +- int command; +- int pdu_size = sizeof(struct smb2_pdu); + int hdr_size = sizeof(struct smb2_hdr); ++ int pdu_size = sizeof(struct smb2_pdu); ++ int command; ++ __u32 calc_len; /* calculated length */ ++ __u64 mid; + + /* + * Add function to do table lookup of StructureSize by command +@@ -154,7 +154,7 @@ smb2_check_message(char *buf, unsigned i + + /* decrypt frame now that it is completely read in */ + spin_lock(&cifs_tcp_ses_lock); +- list_for_each_entry(iter, &srvr->smb_ses_list, smb_ses_list) { ++ list_for_each_entry(iter, &server->smb_ses_list, smb_ses_list) { + if (iter->Suid == le64_to_cpu(thdr->SessionId)) { + ses = iter; + break; +@@ -221,30 +221,33 @@ smb2_check_message(char *buf, unsigned i + } + } + +- clc_len = smb2_calc_size(buf, srvr); ++ calc_len = smb2_calc_size(buf, server); ++ ++ /* For SMB2_IOCTL, OutputOffset and OutputLength are optional, so might ++ * be 0, and not a real miscalculation */ ++ if (command == SMB2_IOCTL_HE && calc_len == 0) ++ return 0; + +- if (shdr->Command == SMB2_NEGOTIATE) +- clc_len += get_neg_ctxt_len(shdr, len, clc_len); ++ if (command == SMB2_NEGOTIATE_HE) ++ calc_len += get_neg_ctxt_len(shdr, len, calc_len); + +- if (len != clc_len) { +- cifs_dbg(FYI, "Calculated size %u length %u mismatch mid %llu\n", +- clc_len, len, mid); ++ if (len != calc_len) { + /* create failed on symlink */ + if (command == SMB2_CREATE_HE && + shdr->Status == STATUS_STOPPED_ON_SYMLINK) + return 0; + /* Windows 7 server returns 24 bytes more */ +- if (clc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) ++ if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE) + return 0; + /* server can return one byte more due to implied bcc[0] */ +- if (clc_len == len + 1) ++ if (calc_len == len + 1) + return 0; + + /* + * Some windows servers (win2016) will pad also the final + * PDU in a compound to 8 bytes. + */ +- if (((clc_len + 7) & ~7) == len) ++ if (((calc_len + 7) & ~7) == len) + return 0; + + /* +@@ -253,12 +256,18 @@ smb2_check_message(char *buf, unsigned i + * SMB2/SMB3 frame length (header + smb2 response specific data) + * Some windows servers also pad up to 8 bytes when compounding. + */ +- if (clc_len < len) ++ if (calc_len < len) + return 0; + +- pr_warn_once( +- "srv rsp too short, len %d not %d. cmd:%d mid:%llu\n", +- len, clc_len, command, mid); ++ /* Only log a message if len was really miscalculated */ ++ if (unlikely(cifsFYI)) ++ cifs_dbg(FYI, "Server response too short: calculated " ++ "length %u doesn't match read length %u (cmd=%d, mid=%llu)\n", ++ calc_len, len, command, mid); ++ else ++ pr_warn("Server response too short: calculated length " ++ "%u doesn't match read length %u (cmd=%d, mid=%llu)\n", ++ calc_len, len, command, mid); + + return 1; + } diff --git a/queue-5.15/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch b/queue-5.15/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch new file mode 100644 index 0000000000..783d186238 --- /dev/null +++ b/queue-5.15/staging-rtl8723bs-core-move-constants-to-right-side-in-comparison.patch @@ -0,0 +1,56 @@ +From stable+bounces-274664-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:19 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 21:34:06 -0400 +Subject: staging: rtl8723bs: core: move constants to right side in comparison +To: stable@vger.kernel.org +Cc: William Hansen-Baird , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260715013408.4110029-3-sashal@kernel.org> + +From: William Hansen-Baird + +[ Upstream commit cf0f2680c30d4b438a5e84b73dc70be405936b54 ] + +Move constants to right side in if-statement conditions. + +Signed-off-by: William Hansen-Baird +Link: https://patch.msgid.link/20251224100329.762141-3-william.hansen.baird@gmail.com +Signed-off-by: Greg Kroah-Hartman +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 +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -1028,7 +1028,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; +@@ -1038,7 +1038,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 +@@ -1538,7 +1538,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-5.15/staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch b/queue-5.15/staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch new file mode 100644 index 0000000000..4de9528dee --- /dev/null +++ b/queue-5.15/staging-rtl8723bs-fix-heap-buffer-overflow-in-rtw_cfg80211_set_wpa_ie.patch @@ -0,0 +1,66 @@ +From stable+bounces-274586-greg=kroah.com@vger.kernel.org Wed Jul 15 00:05:51 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 18:04:26 -0400 +Subject: staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie() +To: stable@vger.kernel.org +Cc: Alexandru Hossu , stable , Luka Gejak , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260714220426.3334125-3-sashal@kernel.org> + +From: Alexandru Hossu + +[ Upstream commit 5a752a616e756844388a1a45404db9fc29fec655 ] + +supplicant_ie is a 256-byte array in struct security_priv. The WPA and +WPA2 IE copy paths use: + + memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2); + +where wpa_ielen is the raw IE length field (u8, 0-255). When a local user +supplies a connect request via nl80211 with a crafted WPA IE of length 255, +wpa_ielen + 2 equals 257, overflowing the 256-byte buffer by one byte into +the adjacent last_mic_err_time field. + +rtw_parse_wpa_ie() does not prevent this: its length consistency check +compares *(wpa_ie+1) against (u8)(wpa_ie_len-2), which is (u8)(255) == 255 +when wpa_ie_len = 257, so the check passes silently. + +Add explicit bounds checks for both the WPA and WPA2 paths before the +memcpy, rejecting any IE whose total size (wpa_ielen + 2) exceeds the +supplicant_ie buffer. + +Fixes: 554c0a3abf21 ("staging: Add rtl8723bs sdio wifi driver") +Cc: stable +Reviewed-by: Luka Gejak +Signed-off-by: Alexandru Hossu +Link: https://patch.msgid.link/20260522004531.1038924-4-hossu.alexandru@gmail.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c ++++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +@@ -1481,6 +1481,10 @@ static int rtw_cfg80211_set_wpa_ie(struc + + pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen); + if (pwpa && wpa_ielen > 0) { ++ if (wpa_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) { ++ ret = -EINVAL; ++ goto exit; ++ } + if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { + padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; +@@ -1490,6 +1494,10 @@ static int rtw_cfg80211_set_wpa_ie(struc + + pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen); + if (pwpa2 && wpa2_ielen > 0) { ++ if (wpa2_ielen + 2 > sizeof(padapter->securitypriv.supplicant_ie)) { ++ ret = -EINVAL; ++ goto exit; ++ } + if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { + padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; diff --git a/queue-5.15/staging-rtl8723bs-fix-indentation-issues.patch b/queue-5.15/staging-rtl8723bs-fix-indentation-issues.patch new file mode 100644 index 0000000000..461e1072af --- /dev/null +++ b/queue-5.15/staging-rtl8723bs-fix-indentation-issues.patch @@ -0,0 +1,56 @@ +From stable+bounces-274584-greg=kroah.com@vger.kernel.org Wed Jul 15 00:05:18 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 18:04:24 -0400 +Subject: staging: rtl8723bs: Fix indentation issues +To: stable@vger.kernel.org +Cc: Franziska Naepelt , Franziska Naepelt , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260714220426.3334125-1-sashal@kernel.org> + +From: Franziska Naepelt + +[ Upstream commit b59cba2309b16fa364df03e3693b8d45c3fadbd6 ] + +Fix the following checkpatch indentation issues: +- WARNING: suspect code indent for conditional statements (32, 48) +- WARNING: suspect code indent for conditional statements (24, 24) +- ERROR: code indent should use tabs where possible + +Signed-off-by: Franziska Naepelt +Link: https://lore.kernel.org/r/20230619180351.18925-1-franziska.naepelt@gmail.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 5a752a616e75 ("staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c ++++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +@@ -582,7 +582,7 @@ static int rtw_cfg80211_ap_set_encryptio + + psecuritypriv->dot118021XGrpPrivacy = _WEP40_; + if (param->u.crypt.key_len == 13) +- psecuritypriv->dot118021XGrpPrivacy = _WEP104_; ++ psecuritypriv->dot118021XGrpPrivacy = _WEP104_; + + } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { + psecuritypriv->dot118021XGrpPrivacy = _TKIP_; +@@ -1305,7 +1305,7 @@ static int cfg80211_rtw_scan(struct wiph + } else if (request->n_channels <= 4) { + for (j = request->n_channels - 1; j >= 0; j--) + for (i = 0; i < survey_times; i++) +- memcpy(&ch[j*survey_times+i], &ch[j], sizeof(struct rtw_ieee80211_channel)); ++ memcpy(&ch[j*survey_times+i], &ch[j], sizeof(struct rtw_ieee80211_channel)); + _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, ch, survey_times * request->n_channels); + } else { + _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, NULL, 0); +@@ -2805,7 +2805,7 @@ int rtw_wdev_alloc(struct adapter *padap + wdev->netdev = pnetdev; + + wdev->iftype = NL80211_IFTYPE_STATION; /* will be init in rtw_hal_init() */ +- /* Must sync with _rtw_init_mlme_priv() */ ++ /* Must sync with _rtw_init_mlme_priv() */ + /* pmlmepriv->fw_state = WIFI_STATION_STATE */ + padapter->rtw_wdev = wdev; + pnetdev->ieee80211_ptr = wdev; diff --git a/queue-5.15/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch b/queue-5.15/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch new file mode 100644 index 0000000000..f779f55fe0 --- /dev/null +++ b/queue-5.15/staging-rtl8723bs-fix-oob-reads-in-rtw_get_sec_ie-rtw_get_wapi_ie-and-rtw_get_wps_attr.patch @@ -0,0 +1,94 @@ +From stable+bounces-274666-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:20 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 21:34:08 -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 , stable , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260715013408.4110029-5-sashal@kernel.org> + +From: Alexandru Hossu + +[ 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 +Signed-off-by: Alexandru Hossu +Link: https://patch.msgid.link/20260522004531.1038924-8-hossu.alexandru@gmail.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -583,9 +583,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) +@@ -618,9 +623,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); +@@ -722,6 +732,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; +@@ -732,6 +745,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-5.15/staging-rtl8723bs-fix-space-issues.patch b/queue-5.15/staging-rtl8723bs-fix-space-issues.patch new file mode 100644 index 0000000000..b3e61cc92e --- /dev/null +++ b/queue-5.15/staging-rtl8723bs-fix-space-issues.patch @@ -0,0 +1,255 @@ +From stable+bounces-274585-greg=kroah.com@vger.kernel.org Wed Jul 15 00:04:53 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 18:04:25 -0400 +Subject: staging: rtl8723bs: Fix space issues +To: stable@vger.kernel.org +Cc: Franziska Naepelt , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260714220426.3334125-2-sashal@kernel.org> + +From: Franziska Naepelt + +[ Upstream commit c4b811b9361b0d7ea167a79587f5ec0a13fd8863 ] + +Fix the following checkpatch space issues: +- CHECK: spaces preferred around that '*' (ctx:VxV) +- CHECK: spaces preferred around that '+' (ctx:VxV) +- CHECK: spaces preferred around that '-' (ctx:VxV) +- CHECK: spaces preferred around that '|' (ctx:VxV) +- CHECK: No space is necessary after a cast +- WARNING: please, no spaces at the start of a line + +Signed-off-by: Franziska Naepelt +Link: https://lore.kernel.org/r/20230701102538.5359-1-franziska.naepelt@googlemail.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 5a752a616e75 ("staging: rtl8723bs: fix heap buffer overflow in rtw_cfg80211_set_wpa_ie()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c | 76 +++++++++++----------- + 1 file changed, 38 insertions(+), 38 deletions(-) + +--- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c ++++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +@@ -95,14 +95,14 @@ static struct ieee80211_channel rtw_2ghz + static void rtw_2g_channels_init(struct ieee80211_channel *channels) + { + memcpy((void *)channels, (void *)rtw_2ghz_channels, +- sizeof(struct ieee80211_channel)*RTW_2G_CHANNELS_NUM ++ sizeof(struct ieee80211_channel) * RTW_2G_CHANNELS_NUM + ); + } + + static void rtw_2g_rates_init(struct ieee80211_rate *rates) + { + memcpy(rates, rtw_g_rates, +- sizeof(struct ieee80211_rate)*RTW_G_RATES_NUM ++ sizeof(struct ieee80211_rate) * RTW_G_RATES_NUM + ); + } + +@@ -126,8 +126,8 @@ static struct ieee80211_supported_band * + if (!spt_band) + goto exit; + +- spt_band->channels = (struct ieee80211_channel *)(((u8 *)spt_band)+sizeof(struct ieee80211_supported_band)); +- spt_band->bitrates = (struct ieee80211_rate *)(((u8 *)spt_band->channels)+sizeof(struct ieee80211_channel)*n_channels); ++ spt_band->channels = (struct ieee80211_channel *)(((u8 *)spt_band) + sizeof(struct ieee80211_supported_band)); ++ spt_band->bitrates = (struct ieee80211_rate *)(((u8 *)spt_band->channels) + sizeof(struct ieee80211_channel) * n_channels); + spt_band->band = band; + spt_band->n_channels = n_channels; + spt_band->n_bitrates = n_bitrates; +@@ -247,10 +247,10 @@ struct cfg80211_bss *rtw_cfg80211_inform + u32 wpsielen = 0; + u8 *wpsie = NULL; + +- wpsie = rtw_get_wps_ie(pnetwork->network.ies+_FIXED_IE_LENGTH_, pnetwork->network.ie_length-_FIXED_IE_LENGTH_, NULL, &wpsielen); ++ wpsie = rtw_get_wps_ie(pnetwork->network.ies + _FIXED_IE_LENGTH_, pnetwork->network.ie_length - _FIXED_IE_LENGTH_, NULL, &wpsielen); + + if (wpsie && wpsielen > 0) +- psr = rtw_get_wps_attr_content(wpsie, wpsielen, WPS_ATTR_SELECTED_REGISTRAR, (u8 *)(&sr), NULL); ++ psr = rtw_get_wps_attr_content(wpsie, wpsielen, WPS_ATTR_SELECTED_REGISTRAR, (u8 *)(&sr), NULL); + + if (sr != 0) { + /* it means under processing WPS */ +@@ -277,9 +277,9 @@ struct cfg80211_bss *rtw_cfg80211_inform + /* We've set wiphy's signal_type as CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm) */ + if (check_fwstate(pmlmepriv, _FW_LINKED) == true && + is_same_network(&pmlmepriv->cur_network.network, &pnetwork->network, 0)) { +- notify_signal = 100*translate_percentage_to_dbm(padapter->recvpriv.signal_strength);/* dbm */ ++ notify_signal = 100 * translate_percentage_to_dbm(padapter->recvpriv.signal_strength);/* dbm */ + } else { +- notify_signal = 100*translate_percentage_to_dbm(pnetwork->network.phy_info.signal_strength);/* dbm */ ++ notify_signal = 100 * translate_percentage_to_dbm(pnetwork->network.phy_info.signal_strength);/* dbm */ + } + + buf = kzalloc(MAX_BSSINFO_LEN, GFP_ATOMIC); +@@ -453,20 +453,20 @@ check_bss: + roam_info.channel = notify_channel; + roam_info.bssid = cur_network->network.mac_address; + roam_info.req_ie = +- pmlmepriv->assoc_req+sizeof(struct ieee80211_hdr_3addr)+2; ++ pmlmepriv->assoc_req + sizeof(struct ieee80211_hdr_3addr) + 2; + roam_info.req_ie_len = +- pmlmepriv->assoc_req_len-sizeof(struct ieee80211_hdr_3addr)-2; ++ pmlmepriv->assoc_req_len - sizeof(struct ieee80211_hdr_3addr) - 2; + roam_info.resp_ie = +- pmlmepriv->assoc_rsp+sizeof(struct ieee80211_hdr_3addr)+6; ++ pmlmepriv->assoc_rsp + sizeof(struct ieee80211_hdr_3addr) + 6; + roam_info.resp_ie_len = +- pmlmepriv->assoc_rsp_len-sizeof(struct ieee80211_hdr_3addr)-6; ++ pmlmepriv->assoc_rsp_len - sizeof(struct ieee80211_hdr_3addr) - 6; + cfg80211_roamed(padapter->pnetdev, &roam_info, GFP_ATOMIC); + } else { + cfg80211_connect_result(padapter->pnetdev, cur_network->network.mac_address +- , pmlmepriv->assoc_req+sizeof(struct ieee80211_hdr_3addr)+2 +- , pmlmepriv->assoc_req_len-sizeof(struct ieee80211_hdr_3addr)-2 +- , pmlmepriv->assoc_rsp+sizeof(struct ieee80211_hdr_3addr)+6 +- , pmlmepriv->assoc_rsp_len-sizeof(struct ieee80211_hdr_3addr)-6 ++ , pmlmepriv->assoc_req + sizeof(struct ieee80211_hdr_3addr) + 2 ++ , pmlmepriv->assoc_req_len - sizeof(struct ieee80211_hdr_3addr) - 2 ++ , pmlmepriv->assoc_rsp + sizeof(struct ieee80211_hdr_3addr) + 6 ++ , pmlmepriv->assoc_rsp_len - sizeof(struct ieee80211_hdr_3addr) - 6 + , WLAN_STATUS_SUCCESS, GFP_ATOMIC); + } + } +@@ -721,7 +721,7 @@ static int rtw_cfg80211_set_encryption(s + param->u.crypt.err = 0; + param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0'; + +- if (param_len < (u32) ((u8 *) param->u.crypt.key - (u8 *) param) + param->u.crypt.key_len) { ++ if (param_len < (u32)((u8 *)param->u.crypt.key - (u8 *)param) + param->u.crypt.key_len) { + ret = -EINVAL; + goto exit; + } +@@ -1035,10 +1035,10 @@ static int cfg80211_rtw_get_station(stru + } + + /* for Ad-Hoc/AP mode */ +- if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) +- || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) +- || check_fwstate(pmlmepriv, WIFI_AP_STATE)) +- && check_fwstate(pmlmepriv, _FW_LINKED)) { ++ if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || ++ check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) || ++ check_fwstate(pmlmepriv, WIFI_AP_STATE)) && ++ check_fwstate(pmlmepriv, _FW_LINKED)) { + /* TODO: should acquire station info... */ + } + +@@ -1237,7 +1237,7 @@ static int cfg80211_rtw_scan(struct wiph + spin_unlock_bh(&pwdev_priv->scan_req_lock); + + if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { +- if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS|_FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true) { ++ if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS | _FW_UNDER_SURVEY | _FW_UNDER_LINKING) == true) { + need_indicate_scan_done = true; + goto check_need_indicate_scan_done; + } +@@ -1291,7 +1291,7 @@ static int cfg80211_rtw_scan(struct wiph + } + + /* parsing channels, n_channels */ +- memset(ch, 0, sizeof(struct rtw_ieee80211_channel)*RTW_CHANNEL_SCAN_AMOUNT); ++ memset(ch, 0, sizeof(struct rtw_ieee80211_channel) * RTW_CHANNEL_SCAN_AMOUNT); + for (i = 0; i < request->n_channels && i < RTW_CHANNEL_SCAN_AMOUNT; i++) { + ch[i].hw_value = request->channels[i]->hw_value; + ch[i].flags = request->channels[i]->flags; +@@ -1305,7 +1305,7 @@ static int cfg80211_rtw_scan(struct wiph + } else if (request->n_channels <= 4) { + for (j = request->n_channels - 1; j >= 0; j--) + for (i = 0; i < survey_times; i++) +- memcpy(&ch[j*survey_times+i], &ch[j], sizeof(struct rtw_ieee80211_channel)); ++ memcpy(&ch[j * survey_times + i], &ch[j], sizeof(struct rtw_ieee80211_channel)); + _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, ch, survey_times * request->n_channels); + } else { + _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, NULL, 0); +@@ -1461,7 +1461,7 @@ static int rtw_cfg80211_set_wpa_ie(struc + goto exit; + } + +- if (ielen > MAX_WPA_IE_LEN+MAX_WPS_IE_LEN+MAX_P2P_IE_LEN) { ++ if (ielen > MAX_WPA_IE_LEN + MAX_WPS_IE_LEN + MAX_P2P_IE_LEN) { + ret = -EINVAL; + goto exit; + } +@@ -1481,19 +1481,19 @@ static int rtw_cfg80211_set_wpa_ie(struc + + pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen); + if (pwpa && wpa_ielen > 0) { +- if (rtw_parse_wpa_ie(pwpa, wpa_ielen+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { ++ if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { + padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK; +- memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen+2); ++ memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2); + } + } + + pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen); + if (pwpa2 && wpa2_ielen > 0) { +- if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen+2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { ++ if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) { + padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; + padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK; +- memcpy(padapter->securitypriv.supplicant_ie, &pwpa2[0], wpa2_ielen+2); ++ memcpy(padapter->securitypriv.supplicant_ie, &pwpa2[0], wpa2_ielen + 2); + } + } + +@@ -1891,7 +1891,7 @@ static int cfg80211_rtw_set_pmksa(struct + + memcpy(psecuritypriv->PMKIDList[index].PMKID, (u8 *)pmksa->pmkid, WLAN_PMKID_LEN); + psecuritypriv->PMKIDList[index].bUsed = true; +- psecuritypriv->PMKIDIndex = index+1; ++ psecuritypriv->PMKIDIndex = index + 1; + blInserted = true; + break; + } +@@ -2090,8 +2090,8 @@ static netdev_tx_t rtw_cfg80211_monitor_ + /* Use the real net device to transmit the packet */ + return _rtw_xmit_entry(skb, padapter->pnetdev); + +- } else if ((frame_control & (IEEE80211_FCTL_FTYPE|IEEE80211_FCTL_STYPE)) == +- (IEEE80211_FTYPE_MGMT|IEEE80211_STYPE_ACTION)) { ++ } else if ((frame_control & (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) == ++ (IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION)) { + /* only for action frames */ + struct xmit_frame *pmgntframe; + struct pkt_attrib *pattrib; +@@ -2205,7 +2205,7 @@ static int rtw_cfg80211_add_monitor_if(s + goto out; + + *ndev = pwdev_priv->pmon_ndev = mon_ndev; +- memcpy(pwdev_priv->ifname_mon, name, IFNAMSIZ+1); ++ memcpy(pwdev_priv->ifname_mon, name, IFNAMSIZ + 1); + + out: + if (ret && mon_wdev) { +@@ -2299,14 +2299,14 @@ static int rtw_add_beacon(struct adapter + if (head_len < 24) + return -EINVAL; + +- pbuf = rtw_zmalloc(head_len+tail_len); ++ pbuf = rtw_zmalloc(head_len + tail_len); + if (!pbuf) + return -ENOMEM; + +- memcpy(pbuf, (void *)head+24, head_len-24);/* 24 =beacon header len. */ +- memcpy(pbuf+head_len-24, (void *)tail, tail_len); ++ memcpy(pbuf, (void *)head + 24, head_len - 24);/* 24 =beacon header len. */ ++ memcpy(pbuf + head_len - 24, (void *)tail, tail_len); + +- len = head_len+tail_len-24; ++ len = head_len + tail_len - 24; + + /* check wps ie if inclued */ + rtw_get_wps_ie(pbuf + _FIXED_IE_LENGTH_, len - _FIXED_IE_LENGTH_, NULL, &wps_ielen); +@@ -2595,7 +2595,7 @@ static int cfg80211_rtw_mgmt_tx(struct w + pwdev_priv = adapter_wdev_data(padapter); + + /* cookie generation */ +- *cookie = (unsigned long) buf; ++ *cookie = (unsigned long)buf; + + /* indicate ack before issue frame to avoid racing with rsp frame */ + rtw_cfg80211_mgmt_tx_status(padapter, *cookie, buf, len, ack, GFP_KERNEL); diff --git a/queue-5.15/staging-rtl8723bs-fix-spaces-around-binary-operators.patch b/queue-5.15/staging-rtl8723bs-fix-spaces-around-binary-operators.patch new file mode 100644 index 0000000000..2b48fda05e --- /dev/null +++ b/queue-5.15/staging-rtl8723bs-fix-spaces-around-binary-operators.patch @@ -0,0 +1,206 @@ +From stable+bounces-274665-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:22 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 21:34:07 -0400 +Subject: staging: rtl8723bs: fix spaces around binary operators +To: stable@vger.kernel.org +Cc: Nikolay Kulikov , Ethan Tidmore , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260715013408.4110029-4-sashal@kernel.org> + +From: Nikolay Kulikov + +[ Upstream commit 0c9d1b56f9af0762a5be5118e1bb962f23784dc4 ] + +Add missing spaces and fix line length to comply with kernel coding +style. + +Signed-off-by: Nikolay Kulikov +Reviewed-by: Ethan Tidmore +Link: https://patch.msgid.link/20260221172751.52329-1-nikolayof23@gmail.com +Signed-off-by: Greg Kroah-Hartman +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 +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -454,10 +454,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; + +@@ -517,7 +517,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; +@@ -585,18 +585,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 */ + } + } + +@@ -620,9 +620,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 */ +@@ -631,10 +632,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 */ + } + } + } +@@ -683,20 +684,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; +@@ -774,12 +775,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; +@@ -1025,20 +1026,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; +@@ -1107,21 +1113,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-5.15/staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch b/queue-5.15/staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch new file mode 100644 index 0000000000..46e245aa67 --- /dev/null +++ b/queue-5.15/staging-rtl8723bs-remove-redundant-braces-in-if-statements.patch @@ -0,0 +1,74 @@ +From stable+bounces-274663-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:17 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 21:34:05 -0400 +Subject: staging: rtl8723bs: remove redundant braces in if statements +To: stable@vger.kernel.org +Cc: Sevinj Aghayeva , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260715013408.4110029-2-sashal@kernel.org> + +From: Sevinj Aghayeva + +[ Upstream commit 94579b02720bb63096312c4bc7694fd2f6df7cbb ] + +Adhere to Linux kernel coding style. + +Reported by checkpatch: + +WARNING: braces {} are not necessary for single statement blocks + +Signed-off-by: Sevinj Aghayeva +Link: https://lore.kernel.org/r/20220331113245.GA425141@euclid +Signed-off-by: Greg Kroah-Hartman +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 +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 12 ++++-------- + 1 file changed, 4 insertions(+), 8 deletions(-) + +--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c ++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +@@ -341,9 +341,8 @@ int rtw_generate_ie(struct registry_priv + + ie = rtw_set_ie(ie, WLAN_EID_IBSS_PARAMS, 2, (u8 *)&(pdev_network->configuration.atim_window), &sz); + +- if (rateLen > 8) { ++ if (rateLen > 8) + ie = rtw_set_ie(ie, WLAN_EID_EXT_SUPP_RATES, (rateLen - 8), (pdev_network->supported_rates + 8), &sz); +- } + + /* HT Cap. */ + if ((pregistrypriv->wireless_mode & WIRELESS_11_24N) && +@@ -372,9 +371,8 @@ unsigned char *rtw_get_wpa_ie(unsigned c + + if (pbuf) { + /* check if oui matches... */ +- if (memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type))) { ++ if (memcmp((pbuf + 2), wpa_oui_type, sizeof(wpa_oui_type))) + goto check_next_ie; +- } + + /* check version... */ + memcpy((u8 *)&le_tmp, (pbuf + 6), sizeof(val16)); +@@ -499,9 +497,8 @@ int rtw_parse_wpa_ie(u8 *wpa_ie, int wpa + if (is_8021x) { + if (left >= 6) { + pos += 2; +- if (!memcmp(pos, SUITE_1X, 4)) { ++ if (!memcmp(pos, SUITE_1X, 4)) + *is_8021x = 1; +- } + } + } + +@@ -520,9 +517,8 @@ 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; + pos += 4; diff --git a/queue-5.15/staging-rtl8723bs-remove-redundant-else-branches.patch b/queue-5.15/staging-rtl8723bs-remove-redundant-else-branches.patch new file mode 100644 index 0000000000..2ebdb9ca8b --- /dev/null +++ b/queue-5.15/staging-rtl8723bs-remove-redundant-else-branches.patch @@ -0,0 +1,87 @@ +From stable+bounces-274662-greg=kroah.com@vger.kernel.org Wed Jul 15 03:34:17 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 21:34:04 -0400 +Subject: staging: rtl8723bs: Remove redundant else branches. +To: stable@vger.kernel.org +Cc: Sevinj Aghayeva , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260715013408.4110029-1-sashal@kernel.org> + +From: Sevinj Aghayeva + +[ Upstream commit 4610e57a7d2edeef14aa852cd945f9920e1759da ] + +This patch fixes the following checkpatch.pl warning: + +WARNING: else is not generally useful after a break or return + +Signed-off-by: Sevinj Aghayeva +Link: https://lore.kernel.org/r/20220329140904.GA3566@ubuntu +Signed-off-by: Greg Kroah-Hartman +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 +Signed-off-by: Greg Kroah-Hartman +--- + drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 27 ++++++++++--------------- + 1 file changed, 11 insertions(+), 16 deletions(-) + +--- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c ++++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c +@@ -94,16 +94,14 @@ bool rtw_is_cckratesonly_included(u8 *ra + + int rtw_check_network_type(unsigned char *rate, int ratelen, int channel) + { +- if (channel > 14) { ++ if (channel > 14) + return WIRELESS_INVALID; +- } else { /* could be pure B, pure G, or B/G */ +- if (rtw_is_cckratesonly_included(rate)) +- return WIRELESS_11B; +- else if (rtw_is_cckrates_included(rate)) +- return WIRELESS_11BG; +- else +- return WIRELESS_11G; +- } ++ /* could be pure B, pure G, or B/G */ ++ if (rtw_is_cckratesonly_included(rate)) ++ return WIRELESS_11B; ++ if (rtw_is_cckrates_included(rate)) ++ return WIRELESS_11BG; ++ return WIRELESS_11G; + } + + u8 *rtw_set_fixed_ie(unsigned char *pbuf, unsigned int len, unsigned char *source, +@@ -155,10 +153,9 @@ u8 *rtw_get_ie(u8 *pbuf, signed int inde + if (*p == index) { + *len = tmp; + return p; +- } else { +- p += (tmp + 2); +- i += (tmp + 2); + } ++ p += (tmp + 2); ++ i += (tmp + 2); + } + return NULL; + } +@@ -702,9 +699,8 @@ u8 *rtw_get_wps_ie(u8 *in_ie, uint in_le + cnt += in_ie[cnt+1]+2; + + break; +- } else { +- cnt += in_ie[cnt+1]+2; /* goto next */ + } ++ cnt += in_ie[cnt+1]+2; /* goto next */ + } + + return wpsie_ptr; +@@ -753,9 +749,8 @@ u8 *rtw_get_wps_attr(u8 *wps_ie, uint wp + *len_attr = attr_len; + + break; +- } else { +- attr_ptr += attr_len; /* goto next */ + } ++ attr_ptr += attr_len; /* goto next */ + } + + return target_attr_ptr; diff --git a/queue-5.15/treewide-switch-rename-to-timer_delete.patch b/queue-5.15/treewide-switch-rename-to-timer_delete.patch new file mode 100644 index 0000000000..68709b7889 --- /dev/null +++ b/queue-5.15/treewide-switch-rename-to-timer_delete.patch @@ -0,0 +1,37 @@ +From stable+bounces-277175-greg=kroah.com@vger.kernel.org Fri Jul 17 20:13:55 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 14:11:58 -0400 +Subject: treewide: Switch/rename to timer_delete[_sync]() +To: stable@vger.kernel.org +Cc: Thomas Gleixner , Ingo Molnar , Sasha Levin +Message-ID: <20260717181159.2086971-1-sashal@kernel.org> + +From: Thomas Gleixner + +[ Upstream commit 8fa7292fee5c5240402371ea89ab285ec856c916 ] + +timer_delete[_sync]() replaces del_timer[_sync](). Convert the whole tree +over and remove the historical wrapper inlines. + +Conversion was done with coccinelle plus manual fixups where necessary. + +Signed-off-by: Thomas Gleixner +Signed-off-by: Ingo Molnar +Stable-dep-of: 75fe87e19d8a ("HID: appleir: fix UAF on pending key_up_timer in remove()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/hid/hid-appleir.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/hid/hid-appleir.c ++++ b/drivers/hid/hid-appleir.c +@@ -319,7 +319,7 @@ static void appleir_remove(struct hid_de + { + struct appleir *appleir = hid_get_drvdata(hid); + hid_hw_stop(hid); +- del_timer_sync(&appleir->key_up_timer); ++ timer_delete_sync(&appleir->key_up_timer); + } + + static const struct hid_device_id appleir_devices[] = { diff --git a/queue-5.15/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch b/queue-5.15/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch new file mode 100644 index 0000000000..d6c73b5a9a --- /dev/null +++ b/queue-5.15/usb-atm-ueagle-atm-wait-for-pre-firmware-load-in-.disconnect.patch @@ -0,0 +1,175 @@ +From stable+bounces-277533-greg=kroah.com@vger.kernel.org Mon Jul 20 04:26:04 2026 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:25:41 -0400 +Subject: usb: atm: ueagle-atm: wait for pre-firmware load in .disconnect() +To: stable@vger.kernel.org +Cc: Mauricio Faria de Oliveira , stable , syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com, syzbot+306212936b13e520679d@syzkaller.appspotmail.com, syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com, Andrey Tsygunka , Stanislaw Gruszka , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260720022541.2927433-1-sashal@kernel.org> + +From: Mauricio Faria de Oliveira + +[ Upstream commit e2674dfbed8a30d57e2bc872c4bfa6c3eec918bf ] + +ueagle-atm uses the asynchronous request_firmware_nowait() in .probe(), +but does not wait for its completion, not even in .disconnect(); so, if the +device is unplugged meanwhile, its teardown runs concurrently with that. + +Even though this inconsistency is worth addressing on its own, it has also +triggered several bug reports in syzbot over the years (some auto-closed) +where the firmware sysfs fallback mechanism (CONFIG_FW_LOADER_USER_HELPER) +creates a firmware subdirectory in the device directory during its removal, +which might hit unexpected conditions in kernfs, apparently, depending at +which point the add and remove operations raced. (See links.) + +The pattern is: + +usb ?-?: Direct firmware load for ueagle-atm/eagle?.fw failed with error -2 +usb ?-?: Falling back to sysfs fallback for: ueagle-atm/eagle?.fw + +Call trace: + ... + kernfs_create_dir_ns + sysfs_create_dir_ns + create_dir + kobject_add_internal + kobject_add_varg + kobject_add + class_dir_create_and_add + get_device_parent + device_add + fw_load_sysfs_fallback + fw_load_from_user_helper + firmware_fallback_sysfs + _request_firmware + request_firmware_work_func + ... + +(Some variations are observed, after fw_load_sysfs_fallback(), e.g., [1].) + +While the kernfs side is being looked at, the ueagle-atm side can be fixed +by waiting for the pre-firmware load in the .disconnect() handler. + +This change has a similar approach to previous work by Andrey Tsygunka [2] +(wait_for_completion() in .disconnect()), but it is relatively different in +design/implementation; using the Originally-by tag for credit assignment. + +This has been tested with: +- synthetic reproducer to check the error path; +- USB gadget (virtual device) to check the firmware upload path; +- QEMU device emulator to check the device ID re-enumeration path; +(The latter two were written by Claude; no other code/text in this commit.) + +Links (year first reported): + 2025 https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d + 2025 https://syzbot.org/bug?extid=9af8471255ac36e34fd4 + 2024 https://syzbot.org/bug?extid=306212936b13e520679d + 2023 https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 + 2022 https://syzbot.org/bug?extid=782984d6f1701b526edb + 2021 https://syzbot.org/bug?id=f3f221579f4ef7e9691281f3c6f56c05f83e8490 + 2021 https://syzbot.org/bug?id=84d86f0d71394829df6fc53daf6642c045983881 + 2021 https://syzbot.org/bug?id=3302dc1c0e2b9c94f2e8edb404eabc9267bc6f90 + +[1] https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 +[2] https://lore.kernel.org/lkml/20250410093146.3776801-2-aitsygunka@yandex.ru/ + +Cc: stable +Reported-by: syzbot+ce1e5a1b4e086b43e56d@syzkaller.appspotmail.com +Closes: https://syzbot.org/bug?extid=ce1e5a1b4e086b43e56d +Reported-by: syzbot+306212936b13e520679d@syzkaller.appspotmail.com +Closes: https://syzbot.org/bug?extid=306212936b13e520679d +Reported-by: syzbot+457452d30bcdda75ead2@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=457452d30bcdda75ead2 +Originally-by: Andrey Tsygunka +Fixes: b72458a80c75 ("[PATCH] USB: Eagle and ADI 930 usb adsl modem driver") +Assisted-by: Claude:claude-opus-4.7 # usb gadget & qemu device for testing +Signed-off-by: Mauricio Faria de Oliveira +Acked-by: Stanislaw Gruszka +Link: https://patch.msgid.link/20260526-ueagle-atm_req-fw-sync-v3-1-93c01961daaf@igalia.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/atm/ueagle-atm.c | 36 +++++++++++++++++++++++++++++++----- + 1 file changed, 31 insertions(+), 5 deletions(-) + +--- a/drivers/usb/atm/ueagle-atm.c ++++ b/drivers/usb/atm/ueagle-atm.c +@@ -599,7 +599,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; +@@ -670,15 +672,17 @@ err_fw_corrupted: + err: + release_firmware(fw_entry); + uea_leaves(usb); ++ 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_enters(usb); + uea_info(usb, "pre-firmware device, uploading firmware\n"); +@@ -702,7 +706,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); +@@ -2598,8 +2602,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(sizeof(*fw_done), GFP_KERNEL); ++ 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) { +@@ -2630,6 +2649,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); + } + + uea_leaves(usb); diff --git a/queue-5.15/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch b/queue-5.15/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch new file mode 100644 index 0000000000..ffa4903a55 --- /dev/null +++ b/queue-5.15/usb-gadget-f_fs-initialize-reset_work-at-allocation-time.patch @@ -0,0 +1,94 @@ +From sashal@kernel.org Mon Jul 20 15:55:10 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 09:55:05 -0400 +Subject: usb: gadget: f_fs: initialize reset_work at allocation time +To: stable@vger.kernel.org +Cc: "Tyler Baker" , stable , "Loic Poulain" , "Dmitry Baryshkov" , "Srinivas Kandagatla" , "Peter Chen" , "Michał Nazarewicz" , "Greg Kroah-Hartman" , "Sasha Levin" +Message-ID: <20260720135505.1350249-1-sashal@kernel.org> + +From: Tyler Baker + +[ 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 +Signed-off-by: Tyler Baker +Cc: Loic Poulain +Cc: Dmitry Baryshkov +Cc: Srinivas Kandagatla +Tested-by: Loic Poulain +Reviewed-by: Peter Chen +Acked-by: Michał Nazarewicz +Link: https://patch.msgid.link/20260609193635.2284430-1-tyler.baker@oss.qualcomm.com +Signed-off-by: Greg Kroah-Hartman +[ dropped the ffs_func_disable hunk since 6.6 predates the disable-body split and routes disable through ffs_func_set_alt, so removing the single lazy INIT_WORK there covers both paths ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/gadget/function/f_fs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/usb/gadget/function/f_fs.c ++++ b/drivers/usb/gadget/function/f_fs.c +@@ -255,6 +255,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 ****************************************************/ + +@@ -1786,6 +1787,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; +@@ -3342,7 +3344,6 @@ static int ffs_func_set_alt(struct usb_f + + if (ffs->state == FFS_DEACTIVATED) { + ffs->state = FFS_CLOSING; +- INIT_WORK(&ffs->reset_work, ffs_reset_work); + schedule_work(&ffs->reset_work); + return -ENODEV; + } diff --git a/queue-5.15/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch b/queue-5.15/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch new file mode 100644 index 0000000000..63b9e5d997 --- /dev/null +++ b/queue-5.15/usb-gadget-f_fs-tie-read_buffer-lifetime-to-ffs_epfile.patch @@ -0,0 +1,55 @@ +From stable+bounces-278071-greg=kroah.com@vger.kernel.org Mon Jul 20 17:49:03 2026 +From: Sasha Levin +Date: Mon, 20 Jul 2026 11:48:25 -0400 +Subject: usb: gadget: f_fs: Tie read_buffer lifetime to ffs_epfile +To: stable@vger.kernel.org +Cc: Neill Kapron , stable , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260720154825.2322611-1-sashal@kernel.org> + +From: Neill Kapron + +[ 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 +Assisted-by: Antigravity:gemini-3.1-pro +Signed-off-by: Neill Kapron +Link: https://patch.msgid.link/20260619040609.4010746-3-nkapron@google.com +Signed-off-by: Greg Kroah-Hartman +[ adjusted context in ffs_epfiles_destroy() since 6.12 lacks the simple_remove_by_name() rework and still uses dentry-based cleanup ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -1292,7 +1292,6 @@ ffs_epfile_release(struct inode *inode, + + ENTER(); + +- __ffs_epfile_read_buffer_free(epfile); + ffs_data_closed(epfile->ffs); + + return 0; +@@ -1957,6 +1956,7 @@ static void ffs_epfiles_destroy(struct f + + for (; count; --count, ++epfile) { + BUG_ON(mutex_is_locked(&epfile->mutex)); ++ __ffs_epfile_read_buffer_free(epfile); + if (epfile->dentry) { + d_delete(epfile->dentry); + dput(epfile->dentry); diff --git a/queue-5.15/usb-iowarrior-remove-inherent-race-with-minor-number.patch b/queue-5.15/usb-iowarrior-remove-inherent-race-with-minor-number.patch new file mode 100644 index 0000000000..fd31bddfc0 --- /dev/null +++ b/queue-5.15/usb-iowarrior-remove-inherent-race-with-minor-number.patch @@ -0,0 +1,109 @@ +From sashal@kernel.org Sun Jul 19 18:31:17 2026 +From: Sasha Levin +Date: Sun, 19 Jul 2026 12:31:12 -0400 +Subject: usb: iowarrior: remove inherent race with minor number +To: stable@vger.kernel.org +Cc: Oliver Neukum , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260719163113.566274-1-sashal@kernel.org> + +From: Oliver Neukum + +[ Upstream commit 56dd29088c9d9510c48a8ebad2465248fde36551 ] + +The driver saves the minor number it gets upon registration +in its descriptor for debugging purposes. However, there is +inevitably a window between registration and saving the correct +minor in a descriptor. During this window the debugging output +will be wrong. +As wrong debug output is worse than no debug output, just +remove it. + +Signed-off-by: Oliver Neukum +Link: https://patch.msgid.link/20260312094619.1590556-1-oneukum@suse.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: c602254ba4c1 ("USB: iowarrior: fix use-after-free on disconnect race") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/misc/iowarrior.c | 17 +++-------------- + 1 file changed, 3 insertions(+), 14 deletions(-) + +--- a/drivers/usb/misc/iowarrior.c ++++ b/drivers/usb/misc/iowarrior.c +@@ -74,7 +74,6 @@ struct iowarrior { + struct mutex mutex; /* locks this structure */ + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface; /* the interface for this device */ +- unsigned char minor; /* the starting minor number for this device */ + struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */ + struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */ + struct urb *int_in_urb; /* the urb for reading data */ +@@ -246,7 +245,6 @@ static void iowarrior_write_callback(str + */ + static inline void iowarrior_delete(struct iowarrior *dev) + { +- dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor); + kfree(dev->int_in_buffer); + usb_free_urb(dev->int_in_urb); + kfree(dev->read_queue); +@@ -297,9 +295,6 @@ static ssize_t iowarrior_read(struct fil + goto exit; + } + +- dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", +- dev->minor, count); +- + /* read count must be packet size (+ time stamp) */ + if ((count != dev->report_size) + && (count != (dev->report_size + 1))) { +@@ -379,8 +374,6 @@ static ssize_t iowarrior_write(struct fi + retval = -ENODEV; + goto exit; + } +- dev_dbg(&dev->interface->dev, "minor %d, count = %zd\n", +- dev->minor, count); + /* if count is 0 we're already done */ + if (count == 0) { + retval = 0; +@@ -523,9 +516,6 @@ static long iowarrior_ioctl(struct file + goto error_out; + } + +- dev_dbg(&dev->interface->dev, "minor %d, cmd 0x%.4x, arg %ld\n", +- dev->minor, cmd, arg); +- + retval = 0; + io_res = 0; + switch (cmd) { +@@ -672,8 +662,6 @@ static int iowarrior_release(struct inod + if (!dev) + return -ENODEV; + +- dev_dbg(&dev->interface->dev, "minor %d\n", dev->minor); +- + /* lock our device */ + mutex_lock(&dev->mutex); + +@@ -776,6 +764,7 @@ static int iowarrior_probe(struct usb_in + struct usb_host_interface *iface_desc; + int retval = -ENOMEM; + int res; ++ int minor; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL); +@@ -891,12 +880,12 @@ static int iowarrior_probe(struct usb_in + goto error; + } + +- dev->minor = interface->minor; ++ minor = interface->minor; + + /* let the user know what node this device is now attached to */ + dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d " + "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial, +- iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE); ++ iface_desc->desc.bInterfaceNumber, minor - IOWARRIOR_MINOR_BASE); + return retval; + + error: diff --git a/queue-5.15/usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch b/queue-5.15/usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch new file mode 100644 index 0000000000..fdc31d10eb --- /dev/null +++ b/queue-5.15/usb-typec-tcpm-fix-vdm-type-for-enter-mode-commands.patch @@ -0,0 +1,39 @@ +From stable+bounces-277534-greg=kroah.com@vger.kernel.org Mon Jul 20 04:25:52 2026 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:25:44 -0400 +Subject: usb: typec: tcpm: Fix VDM type for Enter Mode commands +To: stable@vger.kernel.org +Cc: Andy Yan , stable , Heikki Krogerus , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260720022544.2927635-1-sashal@kernel.org> + +From: Andy Yan + +[ Upstream commit 9cff680e47632b7723cb19f9c5e63669063c3417 ] + +VDO() second parameter is VDM type (bit 15): 1 for SVDM, 0 for UVDM. +Using 'vdo ? 2 : 1' corrupts SVID low bit when vdo is non-NULL +(2 << 15 = BIT(16)). Enter Mode is always SVDM, hardcode to 1. + +Fixes: 8face9aa57c8 ("usb: typec: Add parameter for the VDO to typec_altmode_enter()") +Cc: stable +Signed-off-by: Andy Yan +Reviewed-by: Heikki Krogerus +Link: https://patch.msgid.link/20260604105059.18750-1-andyshrk@163.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/typec/tcpm/tcpm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/typec/tcpm/tcpm.c ++++ b/drivers/usb/typec/tcpm/tcpm.c +@@ -2223,7 +2223,7 @@ static int tcpm_altmode_enter(struct typ + if (svdm_version < 0) + return svdm_version; + +- header = VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); ++ header = VDO(altmode->svid, 1, svdm_version, CMD_ENTER_MODE); + header |= VDO_OPOS(altmode->mode); + + tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0); diff --git a/queue-5.15/usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch b/queue-5.15/usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch new file mode 100644 index 0000000000..09fb762f51 --- /dev/null +++ b/queue-5.15/usb-typec-ucsi-ccg-fix-use-after-free-of-ucsi-on-remove.patch @@ -0,0 +1,56 @@ +From stable+bounces-277532-greg=kroah.com@vger.kernel.org Mon Jul 20 04:26:01 2026 +From: Sasha Levin +Date: Sun, 19 Jul 2026 22:25:37 -0400 +Subject: usb: typec: ucsi: ccg: Fix use-after-free of ucsi on remove +To: stable@vger.kernel.org +Cc: Fan Wu , stable , Heikki Krogerus , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260720022537.2927284-5-sashal@kernel.org> + +From: Fan Wu + +[ Upstream commit 1f0bdc2884b67de337215079bba166df0cdf4ac5 ] + +The threaded IRQ handler ccg_irq_handler() calls ucsi_notify_common(), +which on a connector-change event calls ucsi_connector_change() and +schedules connector work. In ucsi_ccg_remove(), ucsi_destroy() frees +uc->ucsi (kfree) before free_irq() is called, so a handler invocation +already in flight may access the freed object after ucsi_destroy(). + + CPU 0 (remove) | CPU 1 (threaded IRQ) + ucsi_destroy(uc->ucsi) | ccg_irq_handler() + kfree(ucsi) // FREE | ucsi_notify_common(uc->ucsi) // USE + +Move free_irq() before ucsi_destroy() in the remove path. It is kept +after ucsi_unregister(): ucsi_unregister() cancels connector work whose +handler issues GET_CONNECTOR_STATUS through ucsi_send_command_common(), +which waits for a completion that is signalled from the IRQ handler, so +the IRQ must stay active until that work has been cancelled. + +The probe error path already orders free_irq() before ucsi_destroy(). + +This bug was found by static analysis. + +Fixes: e32fd989ac1c ("usb: typec: ucsi: ccg: Move to the new API") +Cc: stable +Signed-off-by: Fan Wu +Reviewed-by: Heikki Krogerus +Link: https://patch.msgid.link/20260616132011.103279-1-fanwu01@zju.edu.cn +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/typec/ucsi/ucsi_ccg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/usb/typec/ucsi/ucsi_ccg.c ++++ b/drivers/usb/typec/ucsi/ucsi_ccg.c +@@ -1418,8 +1418,8 @@ static int ucsi_ccg_remove(struct i2c_cl + cancel_work_sync(&uc->work); + pm_runtime_disable(uc->dev); + ucsi_unregister(uc->ucsi); +- ucsi_destroy(uc->ucsi); + free_irq(uc->irq, uc); ++ ucsi_destroy(uc->ucsi); + + return 0; + } diff --git a/queue-5.15/vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch b/queue-5.15/vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch new file mode 100644 index 0000000000..9f8eaf7222 --- /dev/null +++ b/queue-5.15/vfio-pci-release-the-vga-arbiter-client-on-register_device-failure.patch @@ -0,0 +1,46 @@ +From stable+bounces-274248-greg=kroah.com@vger.kernel.org Tue Jul 14 15:41:49 2026 +From: Sasha Levin +Date: Tue, 14 Jul 2026 09:36:03 -0400 +Subject: vfio/pci: Release the VGA arbiter client on register_device() failure +To: stable@vger.kernel.org +Cc: Alex Williamson , Kevin Tian , Alex Williamson , Sasha Levin +Message-ID: <20260714133603.2673230-1-sashal@kernel.org> + +From: Alex Williamson + +[ Upstream commit daedde7f024ecf88bc8e832ed40cf2c795f0796a ] + +The re-order in the Fixes commit below displaced vfio_pci_vga_init() as +the last failure point of what is now vfio_pci_core_register_device() +without introducing an unwind for the VGA arbiter registration. + +In current kernels this is mostly benign because vfio_pci_set_decode() +only uses pci_dev state, but the original failure path could leave a +callback with a freed vdev cookie. The stale registration also becomes +unsafe again once the callback follows drvdata to the vfio device. + +Add the required VGA unwind callout. + +Fixes: 4aeec3984ddc ("vfio/pci: Re-order vfio_pci_probe()") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-8 +Signed-off-by: Alex Williamson +Reviewed-by: Kevin Tian +Link: https://lore.kernel.org/r/20260615191241.688297-3-alex.williamson@nvidia.com +Signed-off-by: Alex Williamson +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/vfio/pci/vfio_pci_core.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/vfio/pci/vfio_pci_core.c ++++ b/drivers/vfio/pci/vfio_pci_core.c +@@ -1899,6 +1899,7 @@ int vfio_pci_core_register_device(struct + out_power: + if (!disable_idle_d3) + vfio_pci_set_power_state(vdev, PCI_D0); ++ vfio_pci_vga_uninit(vdev); + out_vf: + vfio_pci_vf_uninit(vdev); + out_group_put: diff --git a/queue-5.15/x.509-fix-validation-of-asn.1-certificate-header.patch b/queue-5.15/x.509-fix-validation-of-asn.1-certificate-header.patch new file mode 100644 index 0000000000..21c3d4773a --- /dev/null +++ b/queue-5.15/x.509-fix-validation-of-asn.1-certificate-header.patch @@ -0,0 +1,44 @@ +From stable+bounces-276824-greg=kroah.com@vger.kernel.org Thu Jul 16 21:49:45 2026 +From: Sasha Levin +Date: Thu, 16 Jul 2026 15:49:38 -0400 +Subject: X.509: Fix validation of ASN.1 certificate header +To: stable@vger.kernel.org +Cc: Lukas Wunner , Sashiko , Ignat Korchagin , Alistair Francis , Herbert Xu , Sasha Levin +Message-ID: <20260716194938.1111666-1-sashal@kernel.org> + +From: Lukas Wunner + +[ Upstream commit 3b626ba431c4501512ad07549310685e07fe4706 ] + +x509_load_certificate_list() seeks to enforce that a certificate starts +with 0x30 0x82 (ASN.1 SEQUENCE tag followed by a length of more than 256 +and less than 65535 bytes). + +But it only enforces that *either* of those two byte values are present, +instead of checking for the *conjunction* of the two values. Fix it. + +Fixes: 631cc66eb9ea ("MODSIGN: Provide module signing public keys to the kernel") +Reported-by: Sashiko +Closes: https://lore.kernel.org/r/20260508033917.B5873C2BCB0@smtp.kernel.org/ +Signed-off-by: Lukas Wunner +Cc: stable@vger.kernel.org # v3.7+ +Reviewed-by: Ignat Korchagin +Reviewed-by: Alistair Francis +Signed-off-by: Herbert Xu +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + certs/common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/certs/common.c ++++ b/certs/common.c +@@ -20,7 +20,7 @@ int load_certificate_list(const u8 cert_ + */ + if (end - p < 4) + goto dodgy_cert; +- if (p[0] != 0x30 && ++ if (p[0] != 0x30 || + p[1] != 0x82) + goto dodgy_cert; + plen = (p[2] << 8) | p[3]; diff --git a/queue-5.15/xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch b/queue-5.15/xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch new file mode 100644 index 0000000000..6214f4cfb9 --- /dev/null +++ b/queue-5.15/xdp-introduce-flags-field-in-xdp_buff-xdp_frame.patch @@ -0,0 +1,109 @@ +From stable+bounces-277353-greg=kroah.com@vger.kernel.org Sat Jul 18 18:33:24 2026 +From: Sasha Levin +Date: Sat, 18 Jul 2026 12:31:11 -0400 +Subject: xdp: introduce flags field in xdp_buff/xdp_frame +To: stable@vger.kernel.org +Cc: Lorenzo Bianconi , Toke Hoiland-Jorgensen , John Fastabend , Jesper Dangaard Brouer , Alexei Starovoitov , Sasha Levin +Message-ID: <20260718163112.3339166-1-sashal@kernel.org> + +From: Lorenzo Bianconi + +[ Upstream commit 2e88d4ff03013937028f5397268b21e10cf68713 ] + +Introduce flags field in xdp_frame and xdp_buffer data structures +to define additional buffer features. At the moment the only +supported buffer feature is frags bit (XDP_FLAGS_HAS_FRAGS). +frags bit is used to specify if this is a linear buffer +(XDP_FLAGS_HAS_FRAGS not set) or a frags frame (XDP_FLAGS_HAS_FRAGS +set). In the latter case the driver is expected to initialize the +skb_shared_info structure at the end of the first buffer to link together +subsequent buffers belonging to the same frame. + +Acked-by: Toke Hoiland-Jorgensen +Acked-by: John Fastabend +Acked-by: Jesper Dangaard Brouer +Signed-off-by: Lorenzo Bianconi +Link: https://lore.kernel.org/r/e389f14f3a162c0a5bc6a2e1aa8dd01a90be117d.1642758637.git.lorenzo@kernel.org +Signed-off-by: Alexei Starovoitov +Stable-dep-of: aa496720618f ("bpf: Reject fragmented frames in devmap") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/net/xdp.h | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +--- a/include/net/xdp.h ++++ b/include/net/xdp.h +@@ -66,6 +66,10 @@ struct xdp_txq_info { + struct net_device *dev; + }; + ++enum xdp_buff_flags { ++ XDP_FLAGS_HAS_FRAGS = BIT(0), /* non-linear xdp buff */ ++}; ++ + struct xdp_buff { + void *data; + void *data_end; +@@ -74,13 +78,30 @@ struct xdp_buff { + struct xdp_rxq_info *rxq; + struct xdp_txq_info *txq; + u32 frame_sz; /* frame size to deduce data_hard_end/reserved tailroom*/ ++ u32 flags; /* supported values defined in xdp_buff_flags */ + }; + ++static __always_inline bool xdp_buff_has_frags(struct xdp_buff *xdp) ++{ ++ return !!(xdp->flags & XDP_FLAGS_HAS_FRAGS); ++} ++ ++static __always_inline void xdp_buff_set_frags_flag(struct xdp_buff *xdp) ++{ ++ xdp->flags |= XDP_FLAGS_HAS_FRAGS; ++} ++ ++static __always_inline void xdp_buff_clear_frags_flag(struct xdp_buff *xdp) ++{ ++ xdp->flags &= ~XDP_FLAGS_HAS_FRAGS; ++} ++ + static __always_inline void + xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq) + { + xdp->frame_sz = frame_sz; + xdp->rxq = rxq; ++ xdp->flags = 0; + } + + static __always_inline void +@@ -122,8 +143,14 @@ struct xdp_frame { + */ + struct xdp_mem_info mem; + struct net_device *dev_rx; /* used by cpumap */ ++ u32 flags; /* supported values defined in xdp_buff_flags */ + }; + ++static __always_inline bool xdp_frame_has_frags(struct xdp_frame *frame) ++{ ++ return !!(frame->flags & XDP_FLAGS_HAS_FRAGS); ++} ++ + #define XDP_BULK_QUEUE_SIZE 16 + struct xdp_frame_bulk { + int count; +@@ -180,6 +207,7 @@ void xdp_convert_frame_to_buff(struct xd + xdp->data_end = frame->data + frame->len; + xdp->data_meta = frame->data - frame->metasize; + xdp->frame_sz = frame->frame_sz; ++ xdp->flags = frame->flags; + } + + static inline +@@ -206,6 +234,7 @@ int xdp_update_frame_from_buff(struct xd + xdp_frame->headroom = headroom - sizeof(*xdp_frame); + xdp_frame->metasize = metasize; + xdp_frame->frame_sz = xdp->frame_sz; ++ xdp_frame->flags = xdp->flags; + + return 0; + } diff --git a/queue-5.15/xfs-remove-dead-code.patch b/queue-5.15/xfs-remove-dead-code.patch new file mode 100644 index 0000000000..53b1691d0a --- /dev/null +++ b/queue-5.15/xfs-remove-dead-code.patch @@ -0,0 +1,113 @@ +From stable+bounces-277236-greg=kroah.com@vger.kernel.org Sat Jul 18 03:33:48 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 21:33:34 -0400 +Subject: xfs: Remove dead code +To: stable@vger.kernel.org +Cc: Jiapeng Chong , Abaci Robot , "Darrick J. Wong" , Dave Chinner , Dave Chinner , Sasha Levin +Message-ID: <20260718013335.2621394-2-sashal@kernel.org> + +From: Jiapeng Chong + +[ Upstream commit e62c720817597f259b81f1ff004eb042293bf046 ] + +Remove tht entire xlog_recover_check_summary() function, this entire +function is dead code and has been for 12 years. + +Reported-by: Abaci Robot +Signed-off-by: Jiapeng Chong +Reviewed-by: Darrick J. Wong +Reviewed-by: Dave Chinner +Signed-off-by: Dave Chinner +Stable-dep-of: cc9af5e461ea ("xfs: use null daddr for unset first bad log block") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/xfs/xfs_log_recover.c | 59 ----------------------------------------------- + 1 file changed, 59 deletions(-) + +--- a/fs/xfs/xfs_log_recover.c ++++ b/fs/xfs/xfs_log_recover.c +@@ -39,13 +39,6 @@ STATIC int + xlog_clear_stale_blocks( + struct xlog *, + xfs_lsn_t); +-#if defined(DEBUG) +-STATIC void +-xlog_recover_check_summary( +- struct xlog *); +-#else +-#define xlog_recover_check_summary(log) +-#endif + STATIC int + xlog_do_recovery_pass( + struct xlog *, xfs_daddr_t, xfs_daddr_t, int, xfs_daddr_t *); +@@ -3382,8 +3375,6 @@ xlog_do_recover( + } + mp->m_alloc_set_aside = xfs_alloc_set_aside(mp); + +- xlog_recover_check_summary(log); +- + /* Normal transactions can now occur */ + clear_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate); + return 0; +@@ -3526,7 +3517,6 @@ xlog_recover_finish( + } + + xlog_recover_process_iunlinks(log); +- xlog_recover_check_summary(log); + + /* + * Recover any CoW staging blocks that are still referenced by the +@@ -3560,52 +3550,3 @@ xlog_recover_cancel( + xlog_recover_cancel_intents(log); + } + +-#if defined(DEBUG) +-/* +- * Read all of the agf and agi counters and check that they +- * are consistent with the superblock counters. +- */ +-STATIC void +-xlog_recover_check_summary( +- struct xlog *log) +-{ +- struct xfs_mount *mp = log->l_mp; +- struct xfs_perag *pag; +- struct xfs_buf *agfbp; +- struct xfs_buf *agibp; +- xfs_agnumber_t agno; +- uint64_t freeblks; +- uint64_t itotal; +- uint64_t ifree; +- int error; +- +- freeblks = 0LL; +- itotal = 0LL; +- ifree = 0LL; +- for_each_perag(mp, agno, pag) { +- error = xfs_read_agf(mp, NULL, pag->pag_agno, 0, &agfbp); +- if (error) { +- xfs_alert(mp, "%s agf read failed agno %d error %d", +- __func__, pag->pag_agno, error); +- } else { +- struct xfs_agf *agfp = agfbp->b_addr; +- +- freeblks += be32_to_cpu(agfp->agf_freeblks) + +- be32_to_cpu(agfp->agf_flcount); +- xfs_buf_relse(agfbp); +- } +- +- error = xfs_read_agi(mp, NULL, pag->pag_agno, &agibp); +- if (error) { +- xfs_alert(mp, "%s agi read failed agno %d error %d", +- __func__, pag->pag_agno, error); +- } else { +- struct xfs_agi *agi = agibp->b_addr; +- +- itotal += be32_to_cpu(agi->agi_count); +- ifree += be32_to_cpu(agi->agi_freecount); +- xfs_buf_relse(agibp); +- } +- } +-} +-#endif /* DEBUG */ diff --git a/queue-5.15/xfs-remove-redundant-assignment-of-mp.patch b/queue-5.15/xfs-remove-redundant-assignment-of-mp.patch new file mode 100644 index 0000000000..f4e37bffe0 --- /dev/null +++ b/queue-5.15/xfs-remove-redundant-assignment-of-mp.patch @@ -0,0 +1,43 @@ +From stable+bounces-277235-greg=kroah.com@vger.kernel.org Sat Jul 18 03:33:43 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 21:33:33 -0400 +Subject: xfs: Remove redundant assignment of mp +To: stable@vger.kernel.org +Cc: Jiapeng Chong , Abaci Robot , "Darrick J. Wong" , Sasha Levin +Message-ID: <20260718013335.2621394-1-sashal@kernel.org> + +From: Jiapeng Chong + +[ Upstream commit f4901a182d33d05a3b7020e2af97c635f6c47959 ] + +mp is being initialized to log->l_mp but this is never read +as record is overwritten later on. Remove the redundant +assignment. + +Cleans up the following clang-analyzer warning: + +fs/xfs/xfs_log_recover.c:3543:20: warning: Value stored to 'mp' during +its initialization is never read [clang-analyzer-deadcode.DeadStores]. + +Reported-by: Abaci Robot +Signed-off-by: Jiapeng Chong +Reviewed-by: Darrick J. Wong +Signed-off-by: Darrick J. Wong +Stable-dep-of: cc9af5e461ea ("xfs: use null daddr for unset first bad log block") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/xfs/xfs_log_recover.c | 2 -- + 1 file changed, 2 deletions(-) + +--- a/fs/xfs/xfs_log_recover.c ++++ b/fs/xfs/xfs_log_recover.c +@@ -3579,8 +3579,6 @@ xlog_recover_check_summary( + uint64_t ifree; + int error; + +- mp = log->l_mp; +- + freeblks = 0LL; + itotal = 0LL; + ifree = 0LL; diff --git a/queue-5.15/xfs-use-null-daddr-for-unset-first-bad-log-block.patch b/queue-5.15/xfs-use-null-daddr-for-unset-first-bad-log-block.patch new file mode 100644 index 0000000000..be2920c0e3 --- /dev/null +++ b/queue-5.15/xfs-use-null-daddr-for-unset-first-bad-log-block.patch @@ -0,0 +1,59 @@ +From stable+bounces-277237-greg=kroah.com@vger.kernel.org Sat Jul 18 03:33:49 2026 +From: Sasha Levin +Date: Fri, 17 Jul 2026 21:33:35 -0400 +Subject: xfs: use null daddr for unset first bad log block +To: stable@vger.kernel.org +Cc: Yousef Alhouseen , "Darrick J. Wong" , syzbot+b7dfbed0c6c2b5e9fd34@syzkaller.appspotmail.com, Carlos Maiolino , Sasha Levin +Message-ID: <20260718013335.2621394-3-sashal@kernel.org> + +From: Yousef Alhouseen + +[ Upstream commit cc9af5e461ea5f6e37738f3f1e41c45a9b7f45d6 ] + +xlog_do_recovery_pass() may return before setting first_bad. The caller +must distinguish that case from an error at a valid log block, including +block zero after the log wraps. + +Initialize first_bad to XFS_BUF_DADDR_NULL and test it explicitly before +treating the error as a torn write. + +Fixes: 7088c4136fa1 ("xfs: detect and trim torn writes during log recovery") +Suggested-by: Darrick J. Wong +Reported-by: syzbot+b7dfbed0c6c2b5e9fd34@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=b7dfbed0c6c2b5e9fd34 +Cc: stable@vger.kernel.org # v4.5 +Signed-off-by: Yousef Alhouseen +Reviewed-by: "Darrick J. Wong" +Signed-off-by: Carlos Maiolino +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/xfs/xfs_log_recover.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/fs/xfs/xfs_log_recover.c ++++ b/fs/xfs/xfs_log_recover.c +@@ -1028,7 +1028,7 @@ xlog_verify_head( + { + struct xlog_rec_header *tmp_rhead; + char *tmp_buffer; +- xfs_daddr_t first_bad; ++ xfs_daddr_t first_bad = XFS_BUF_DADDR_NULL; + xfs_daddr_t tmp_rhead_blk; + int found; + int error; +@@ -1057,7 +1057,8 @@ xlog_verify_head( + */ + error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk, + XLOG_RECOVER_CRCPASS, &first_bad); +- if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) { ++ if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && ++ first_bad != XFS_BUF_DADDR_NULL) { + /* + * We've hit a potential torn write. Reset the error and warn + * about it. +@@ -3549,4 +3550,3 @@ xlog_recover_cancel( + if (xlog_recovery_needed(log)) + xlog_recover_cancel_intents(log); + } +-