From: Greg Kroah-Hartman Date: Tue, 16 Jun 2026 07:10:40 +0000 (+0530) Subject: 5.10-stable patches X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=094273da630fa81a6ba329966730403eba4e05a3;p=thirdparty%2Fkernel%2Fstable-queue.git 5.10-stable patches added patches: acpi-scan-use-acpi_dev_put-in-object-add-error-paths.patch alsa-aloop-fix-peer-runtime-uaf-during-format-change-stop.patch alsa-aoa-i2sbus-clear-stale-prepared-state.patch alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch alsa-aoa-use-guard-for-mutex-locks.patch alsa-core-fix-potential-data-race-at-fasync-handling.patch arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch arm64-tlb-flush-walk-cache-when-unsharing-pmd-tables.patch bluetooth-consolidate-code-around-sk_alloc-into-a-helper-function.patch bluetooth-fix-uaf-in-l2cap_sock_cleanup_listen-vs-l2cap_conn_del.patch bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.patch bluetooth-hci_qca-convert-timeout-from-jiffies-to-ms.patch bluetooth-init-sk_peer_-on-bt_sock_alloc.patch bluetooth-serialize-accept_q-access.patch btrfs-fix-btrfs_ioctl_space_info-slot_count-toctou-which-can-lead-to-info-leak.patch btrfs-fix-missing-last_unlink_trans-update-when-removing-a-directory.patch can-ucan-fix-devres-lifetime.patch can-ucan-fix-typos-in-comments.patch ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch dm-btree-improve-btree-residency.patch dm-thin-fix-metadata-refcount-underflow.patch drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch f2fs-fix-incorrect-file-address-mapping-when-inline-inode-is-unwritten.patch f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch fbcon-avoid-oob-font-access-if-console-rotation-fails.patch hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch hv_netvsc-use-kmap_local_page-in-netvsc_copy_to_send_buf.patch ice-fix-vf-queue-configuration-with-low-mtu-values.patch ktest-fix-the-month-in-the-name-of-the-failure-directory.patch ktest-fixing-indentation-to-match-expected-pattern.patch media-rc-igorplugusb-heed-coherency-rules.patch media-rc-ttusbir-respect-dma-coherency-rules.patch mm-huge_memory-update-file-pmd-counter-before-folio_put.patch mm-hugetlb_cma-round-up-per_node-before-logging-it.patch mptcp-do-not-drop-partial-packets.patch mptcp-pm-add_addr-rtx-fix-potential-data-race.patch mtd-spi-nor-sst-fix-write-enable-before-aai-sequence.patch net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch net-hsr-defer-node-table-free-until-after-rcu-readers.patch net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch net-qrtr-ns-change-servers-radix-tree-to-xarray.patch net-qrtr-ns-free-the-node-during-ctrl_cmd_bye.patch net-qrtr-ns-limit-the-maximum-number-of-lookups.patch net-qrtr-ns-limit-the-total-number-of-nodes.patch net-remove-redundant-if-statements.patch netfilter-nf_queue-hold-bridge-skb-dev-while-queued.patch netfilter-nft_fib-fix-stale-stack-leak-via-the-oifname-register.patch octeontx2-af-add-validation-for-lmac-type.patch octeontx2-af-cgx-add-bounds-check-to-cgx_speed_mbps-index.patch octeontx2-af-replace-deprecated-strncpy-with-strscpy.patch octeontx2-pf-avoid-double-free-of-pool-stack-on-aq-init-failure.patch phy-tegra-xusb-disable-trk-clk-when-not-in-use.patch phy-tegra-xusb-fix-per-pad-high-speed-termination-calibration.patch pmdomain-core-fix-detach-procedure-for-virtual-devices-in-genpd.patch printk-add-print_hex_dump_devel.patch qed-fix-double-free-in-qed_cxt_tables_alloc.patch qed-use-the-bitmap-api-to-simplify-some-functions.patch rdma-move-dma-block-iterator-logic-into-dedicated-files.patch rdma-umem-fix-kernel-doc-warnings.patch rdma-umem-fix-truncation-for-block-sizes-4g.patch sched-use-u64-for-bandwidth-ratio-calculations.patch scsi-target-iscsi-bound-iscsi_encode_text_output-appends-to-rsp_buf.patch scsi-target-iscsi-fix-crc-overread-and-double-free-in-iscsit_handle_text_cmd.patch selftests-mptcp-drop-nanoseconds-width-specifier.patch serial-altera_jtaguart-handle-uart_add_one_port-failures.patch serial-altera_jtaguart-use-platform_get_irq_optional-to-get-the-interrupt.patch serial-qcom-geni-fix-uart_rx_par_en-bit-position.patch smb-client-fix-oob-read-in-smb2_ioctl_query_info-query_info-path.patch smb-client-require-a-full-nfs-mode-sid-before-reading-mode-bits.patch smb-client-use-fullsessionkey-for-aes-256-encryption-key-derivation.patch spi-lantiq-ssc-fix-controller-deregistration.patch spi-qup-fix-error-pointer-deref-after-dma-setup-failure.patch spi-qup-switch-to-use-modern-name.patch spi-st-ssc4-fix-controller-deregistration.patch spi-sun4i-fix-controller-deregistration.patch spi-sun6i-fix-controller-deregistration.patch spi-syncuacer-fix-controller-deregistration.patch spi-tegra114-fix-controller-deregistration.patch spi-tegra20-sflash-fix-controller-deregistration.patch spi-ti-qspi-fix-controller-deregistration.patch spi-topcliff-pch-fix-controller-deregistration.patch spi-uniphier-fix-controller-deregistration.patch spi-zynq-qspi-fix-controller-deregistration.patch thermal-core-fix-thermal-zone-governor-cleanup-issues.patch thunderbolt-property-cap-recursion-depth-in-__tb_property_parse_dir.patch tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch tracing-probes-limit-size-of-event-probe-to-3k.patch tty-serial-qcom-geni-serial-align-define-values.patch tty-serial-qcom-geni-serial-remove-unused-symbols.patch udf-fix-partition-descriptor-append-bookkeeping.patch usb-dwc3-move-guid-programming-after-phy-initialization.patch usb-typec-ucsi-check-if-power-role-change-actually-happened-before-handling.patch usb-typec-ucsi-don-t-update-power_supply-on-power-role-change-if-not-connected.patch use-less-confusing-names-for-iov_iter-direction-initializers.patch wifi-brcmfmac-fix-potential-use-after-free-issue-when-stopping-watchdog-task.patch wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch --- diff --git a/queue-5.10/acpi-scan-use-acpi_dev_put-in-object-add-error-paths.patch b/queue-5.10/acpi-scan-use-acpi_dev_put-in-object-add-error-paths.patch new file mode 100644 index 0000000000..beec44a7d2 --- /dev/null +++ b/queue-5.10/acpi-scan-use-acpi_dev_put-in-object-add-error-paths.patch @@ -0,0 +1,66 @@ +From stable+bounces-245039-greg=kroah.com@vger.kernel.org Sun May 10 21:16:35 2026 +From: Sasha Levin +Date: Sun, 10 May 2026 11:46:27 -0400 +Subject: ACPI: scan: Use acpi_dev_put() in object add error paths +To: stable@vger.kernel.org +Cc: Guangshuo Li , "Rafael J. Wysocki" , Sasha Levin +Message-ID: <20260510154627.158558-1-sashal@kernel.org> + +From: Guangshuo Li + +[ Upstream commit 9c0acc169ac71535477caedea8315f7041c5f07c ] + +After acpi_init_device_object(), the lifetime of struct acpi_device is +managed by the driver core through reference counting. + +Both acpi_add_power_resource() and acpi_add_single_object() call +acpi_init_device_object() and then invoke acpi_device_add(). If that +fails, their error paths call the release callback directly instead of +dropping the device reference through acpi_dev_put(). + +This bypasses the normal device lifetime rules and frees the object +without releasing the reference acquired by device_initialize(), which +may lead to a refcount leak. + +The issue was identified by a static analysis tool I developed and +confirmed by manual review. + +Fix both error paths by using acpi_dev_put() and let the release +callback handle the final cleanup. + +Fixes: 781d737c7466 ("ACPI: Drop power resources driver") +Fixes: 718fb0de8ff88 ("ACPI: fix NULL bug for HID/UID string") +Cc: All applicable +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260413135343.2884481-1-lgs201920130244@gmail.com +Signed-off-by: Rafael J. Wysocki +[ preserved 5.10's `return result;` instead of upstream's `return NULL;` since the function returns int ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/acpi/power.c | 2 +- + drivers/acpi/scan.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/acpi/power.c ++++ b/drivers/acpi/power.c +@@ -977,7 +977,7 @@ int acpi_add_power_resource(acpi_handle + return 0; + + err: +- acpi_release_power_resource(&device->dev); ++ acpi_dev_put(device); + return result; + } + +--- a/drivers/acpi/scan.c ++++ b/drivers/acpi/scan.c +@@ -1679,7 +1679,7 @@ static int acpi_add_single_object(struct + + result = acpi_device_add(device, acpi_device_release); + if (result) { +- acpi_device_release(&device->dev); ++ acpi_dev_put(device); + return result; + } + diff --git a/queue-5.10/alsa-aloop-fix-peer-runtime-uaf-during-format-change-stop.patch b/queue-5.10/alsa-aloop-fix-peer-runtime-uaf-during-format-change-stop.patch new file mode 100644 index 0000000000..9a671ef24e --- /dev/null +++ b/queue-5.10/alsa-aloop-fix-peer-runtime-uaf-during-format-change-stop.patch @@ -0,0 +1,119 @@ +From stable+bounces-245022-greg=kroah.com@vger.kernel.org Sun May 10 19:08:09 2026 +From: Sasha Levin +Date: Sun, 10 May 2026 09:38:00 -0400 +Subject: ALSA: aloop: Fix peer runtime UAF during format-change stop +To: stable@vger.kernel.org +Cc: "Cássio Gabriel" , syzbot+8fa95c41eafbc9d2ff6f@syzkaller.appspotmail.com, "Takashi Iwai" , "Takashi Iwai" , "Sasha Levin" +Message-ID: <20260510133800.4137265-1-sashal@kernel.org> + +From: Cássio Gabriel + +[ Upstream commit e5c33cdc6f402eab8abd36ecf436b22c9d3a8aff ] + +loopback_check_format() may stop the capture side when playback starts +with parameters that no longer match a running capture stream. Commit +826af7fa62e3 ("ALSA: aloop: Fix racy access at PCM trigger") moved +the peer lookup under cable->lock, but the actual snd_pcm_stop() still +runs after dropping that lock. + +A concurrent close can clear the capture entry from cable->streams[] and +detach or free its runtime while the playback trigger path still holds a +stale peer substream pointer. + +Keep a per-cable count of in-flight peer stops before dropping +cable->lock, and make free_cable() wait for those stops before +detaching the runtime. This preserves the existing behavior while +making the peer runtime lifetime explicit. + +Reported-by: syzbot+8fa95c41eafbc9d2ff6f@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=8fa95c41eafbc9d2ff6f +Fixes: 597603d615d2 ("ALSA: introduce the snd-aloop module for the PCM loopback") +Cc: stable@vger.kernel.org +Suggested-by: Takashi Iwai +Signed-off-by: Cássio Gabriel +Link: https://patch.msgid.link/20260424-alsa-aloop-peer-stop-uaf-v2-1-94e68101db8a@gmail.com +Signed-off-by: Takashi Iwai +[ collapsed inc/snd_pcm_stop/dec into the existing inline call site and used spin_lock_irq/unlock_irq instead of scoped_guard ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/drivers/aloop.c | 40 +++++++++++++++++++++++++++------------- + 1 file changed, 27 insertions(+), 13 deletions(-) + +--- a/sound/drivers/aloop.c ++++ b/sound/drivers/aloop.c +@@ -99,6 +99,9 @@ struct loopback_ops { + struct loopback_cable { + spinlock_t lock; + struct loopback_pcm *streams[2]; ++ /* in-flight peer stops running outside cable->lock */ ++ atomic_t stop_count; ++ wait_queue_head_t stop_wait; + struct snd_pcm_hardware hw; + /* flags */ + unsigned int valid; +@@ -342,8 +345,12 @@ static int loopback_check_format(struct + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + return -EIO; + } else { ++ /* close must not free the peer runtime below */ ++ atomic_inc(&cable->stop_count); + snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]-> + substream, SNDRV_PCM_STATE_DRAINING); ++ if (atomic_dec_and_test(&cable->stop_count)) ++ wake_up(&cable->stop_wait); + __notify: + runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]-> + substream->runtime; +@@ -995,24 +1002,29 @@ static void free_cable(struct snd_pcm_su + struct loopback *loopback = substream->private_data; + int dev = get_cable_index(substream); + struct loopback_cable *cable; ++ struct loopback_pcm *dpcm; ++ bool other_alive; + + cable = loopback->cables[substream->number][dev]; + if (!cable) + return; +- if (cable->streams[!substream->stream]) { +- /* other stream is still alive */ +- spin_lock_irq(&cable->lock); +- cable->streams[substream->stream] = NULL; +- spin_unlock_irq(&cable->lock); +- } else { +- struct loopback_pcm *dpcm = substream->runtime->private_data; + +- if (cable->ops && cable->ops->close_cable && dpcm) +- cable->ops->close_cable(dpcm); +- /* free the cable */ +- loopback->cables[substream->number][dev] = NULL; +- kfree(cable); +- } ++ spin_lock_irq(&cable->lock); ++ cable->streams[substream->stream] = NULL; ++ other_alive = cable->streams[!substream->stream] != NULL; ++ spin_unlock_irq(&cable->lock); ++ ++ /* Pair with the stop_count increment in loopback_check_format(). */ ++ wait_event(cable->stop_wait, !atomic_read(&cable->stop_count)); ++ if (other_alive) ++ return; ++ ++ dpcm = substream->runtime->private_data; ++ if (cable->ops && cable->ops->close_cable && dpcm) ++ cable->ops->close_cable(dpcm); ++ /* free the cable */ ++ loopback->cables[substream->number][dev] = NULL; ++ kfree(cable); + } + + static int loopback_jiffies_timer_open(struct loopback_pcm *dpcm) +@@ -1207,6 +1219,8 @@ static int loopback_open(struct snd_pcm_ + goto unlock; + } + spin_lock_init(&cable->lock); ++ atomic_set(&cable->stop_count, 0); ++ init_waitqueue_head(&cable->stop_wait); + cable->hw = loopback_pcm_hardware; + if (loopback->timer_source) + cable->ops = &loopback_snd_timer_ops; diff --git a/queue-5.10/alsa-aoa-i2sbus-clear-stale-prepared-state.patch b/queue-5.10/alsa-aoa-i2sbus-clear-stale-prepared-state.patch new file mode 100644 index 0000000000..fa380fa846 --- /dev/null +++ b/queue-5.10/alsa-aoa-i2sbus-clear-stale-prepared-state.patch @@ -0,0 +1,165 @@ +From stable+bounces-242481-greg=kroah.com@vger.kernel.org Fri May 1 22:58:13 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 13:28:00 -0400 +Subject: ALSA: aoa: i2sbus: clear stale prepared state +To: stable@vger.kernel.org +Cc: "Cássio Gabriel" , "kernel test robot" , "Takashi Iwai" , "Sasha Levin" +Message-ID: <20260501172800.3766490-2-sashal@kernel.org> + +From: Cássio Gabriel + +[ Upstream commit 5ed060d5491597490fb53ec69da3edc4b1e8c165 ] + +The i2sbus PCM code uses pi->active to constrain the sibling stream to +an already prepared duplex format and rate in i2sbus_pcm_open(). + +That state is set from i2sbus_pcm_prepare(), but the current code only +clears it on close. As a result, the sibling stream can inherit stale +constraints after the prepared state has been torn down. + +Clear pi->active when hw_params() or hw_free() tears down the prepared +state, and set it again only after prepare succeeds. + +Replace the stale FIXME in the duplex constraint comment with a description +of the current driver behavior: i2sbus still programs a single shared +transport configuration for both directions, so mixed formats are not +supported in duplex mode. + +Reported-by: kernel test robot +Closes: https://lore.kernel.org/oe-kbuild-all/202604010125.AvkWBYKI-lkp@intel.com/ +Fixes: f3d9478b2ce4 ("[ALSA] snd-aoa: add snd-aoa") +Cc: stable@vger.kernel.org +Signed-off-by: Cássio Gabriel +Link: https://patch.msgid.link/20260331-aoa-i2sbus-clear-stale-active-v2-1-3764ae2889a1@gmail.com +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/aoa/soundbus/i2sbus/pcm.c | 55 ++++++++++++++++++++++++++++++++-------- + 1 file changed, 44 insertions(+), 11 deletions(-) + +--- a/sound/aoa/soundbus/i2sbus/pcm.c ++++ b/sound/aoa/soundbus/i2sbus/pcm.c +@@ -165,17 +165,16 @@ static int i2sbus_pcm_open(struct i2sbus + * currently in use (if any). */ + hw->rate_min = 5512; + hw->rate_max = 192000; +- /* if the other stream is active, then we can only +- * support what it is currently using. +- * FIXME: I lied. This comment is wrong. We can support +- * anything that works with the same serial format, ie. +- * when recording 24 bit sound we can well play 16 bit +- * sound at the same time iff using the same transfer mode. ++ /* If the other stream is already prepared, keep this stream ++ * on the same duplex format and rate. ++ * ++ * i2sbus_pcm_prepare() still programs one shared transport ++ * configuration for both directions, so mixed duplex formats ++ * are not supported here. + */ + if (other->active) { +- /* FIXME: is this guaranteed by the alsa api? */ + hw->formats &= pcm_format_to_bits(i2sdev->format); +- /* see above, restrict rates to the one we already have */ ++ /* Restrict rates to the one already in use. */ + hw->rate_min = i2sdev->rate; + hw->rate_max = i2sdev->rate; + } +@@ -283,6 +282,23 @@ void i2sbus_wait_for_stop_both(struct i2 + } + #endif + ++static void i2sbus_pcm_clear_active(struct i2sbus_dev *i2sdev, int in) ++{ ++ struct pcm_info *pi; ++ ++ guard(mutex)(&i2sdev->lock); ++ ++ get_pcm_info(i2sdev, in, &pi, NULL); ++ pi->active = 0; ++} ++ ++static inline int i2sbus_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, int in) ++{ ++ i2sbus_pcm_clear_active(snd_pcm_substream_chip(substream), in); ++ return 0; ++} ++ + static inline int i2sbus_hw_free(struct snd_pcm_substream *substream, int in) + { + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); +@@ -291,14 +307,27 @@ static inline int i2sbus_hw_free(struct + get_pcm_info(i2sdev, in, &pi, NULL); + if (pi->dbdma_ring.stopping) + i2sbus_wait_for_stop(i2sdev, pi); ++ i2sbus_pcm_clear_active(i2sdev, in); + return 0; + } + ++static int i2sbus_playback_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ return i2sbus_hw_params(substream, params, 0); ++} ++ + static int i2sbus_playback_hw_free(struct snd_pcm_substream *substream) + { + return i2sbus_hw_free(substream, 0); + } + ++static int i2sbus_record_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ return i2sbus_hw_params(substream, params, 1); ++} ++ + static int i2sbus_record_hw_free(struct snd_pcm_substream *substream) + { + return i2sbus_hw_free(substream, 1); +@@ -335,7 +364,6 @@ static int i2sbus_pcm_prepare(struct i2s + return -EINVAL; + + runtime = pi->substream->runtime; +- pi->active = 1; + if (other->active && + ((i2sdev->format != runtime->format) + || (i2sdev->rate != runtime->rate))) +@@ -450,9 +478,11 @@ static int i2sbus_pcm_prepare(struct i2s + + /* early exit if already programmed correctly */ + /* not locking these is fine since we touch them only in this function */ +- if (in_le32(&i2sdev->intfregs->serial_format) == sfr +- && in_le32(&i2sdev->intfregs->data_word_sizes) == dws) ++ if (in_le32(&i2sdev->intfregs->serial_format) == sfr && ++ in_le32(&i2sdev->intfregs->data_word_sizes) == dws) { ++ pi->active = 1; + return 0; ++ } + + /* let's notify the codecs about clocks going away. + * For now we only do mastering on the i2s cell... */ +@@ -490,6 +520,7 @@ static int i2sbus_pcm_prepare(struct i2s + if (cii->codec->switch_clock) + cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE); + ++ pi->active = 1; + return 0; + } + +@@ -746,6 +777,7 @@ static snd_pcm_uframes_t i2sbus_playback + static const struct snd_pcm_ops i2sbus_playback_ops = { + .open = i2sbus_playback_open, + .close = i2sbus_playback_close, ++ .hw_params = i2sbus_playback_hw_params, + .hw_free = i2sbus_playback_hw_free, + .prepare = i2sbus_playback_prepare, + .trigger = i2sbus_playback_trigger, +@@ -814,6 +846,7 @@ static snd_pcm_uframes_t i2sbus_record_p + static const struct snd_pcm_ops i2sbus_record_ops = { + .open = i2sbus_record_open, + .close = i2sbus_record_close, ++ .hw_params = i2sbus_record_hw_params, + .hw_free = i2sbus_record_hw_free, + .prepare = i2sbus_record_prepare, + .trigger = i2sbus_record_trigger, diff --git a/queue-5.10/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch b/queue-5.10/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch new file mode 100644 index 0000000000..441eae7fd8 --- /dev/null +++ b/queue-5.10/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch @@ -0,0 +1,83 @@ +From stable+bounces-242492-greg=kroah.com@vger.kernel.org Sat May 2 00:18:29 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 14:48:19 -0400 +Subject: ALSA: aoa: Skip devices with no codecs in i2sbus_resume() +To: stable@vger.kernel.org +Cc: Thorsten Blum , Takashi Iwai , Sasha Levin +Message-ID: <20260501184819.3923492-2-sashal@kernel.org> + +From: Thorsten Blum + +[ Upstream commit fd7df93013c5118812e63a52635dc6c3a805a1de ] + +In i2sbus_resume(), skip devices with an empty codec list, which avoids +using an uninitialized 'sysclock_factor' in the 32-bit format path in +i2sbus_pcm_prepare(). + +In i2sbus_pcm_prepare(), replace two list_for_each_entry() loops with a +single list_first_entry() now that the codec list is guaranteed to be +non-empty by all callers. + +Fixes: f3d9478b2ce4 ("[ALSA] snd-aoa: add snd-aoa") +Cc: stable@vger.kernel.org +Signed-off-by: Thorsten Blum +Link: https://patch.msgid.link/20260310102921.210109-3-thorsten.blum@linux.dev +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/aoa/soundbus/i2sbus/core.c | 3 +++ + sound/aoa/soundbus/i2sbus/pcm.c | 16 +++++----------- + 2 files changed, 8 insertions(+), 11 deletions(-) + +--- a/sound/aoa/soundbus/i2sbus/core.c ++++ b/sound/aoa/soundbus/i2sbus/core.c +@@ -411,6 +411,9 @@ static int i2sbus_resume(struct macio_de + int err, ret = 0; + + list_for_each_entry(i2sdev, &control->list, item) { ++ if (list_empty(&i2sdev->sound.codec_list)) ++ continue; ++ + /* reset i2s bus format etc. */ + i2sbus_pcm_prepare_both(i2sdev); + +--- a/sound/aoa/soundbus/i2sbus/pcm.c ++++ b/sound/aoa/soundbus/i2sbus/pcm.c +@@ -411,6 +411,9 @@ static int i2sbus_pcm_prepare(struct i2s + /* set stop command */ + command->command = cpu_to_le16(DBDMA_STOP); + ++ cii = list_first_entry(&i2sdev->sound.codec_list, ++ struct codec_info_item, list); ++ + /* ok, let's set the serial format and stuff */ + switch (runtime->format) { + /* 16 bit formats */ +@@ -418,13 +421,7 @@ static int i2sbus_pcm_prepare(struct i2s + case SNDRV_PCM_FORMAT_U16_BE: + /* FIXME: if we add different bus factors we need to + * do more here!! */ +- bi.bus_factor = 0; +- list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { +- bi.bus_factor = cii->codec->bus_factor; +- break; +- } +- if (!bi.bus_factor) +- return -ENODEV; ++ bi.bus_factor = cii->codec->bus_factor; + input_16bit = 1; + break; + case SNDRV_PCM_FORMAT_S32_BE: +@@ -438,10 +435,7 @@ static int i2sbus_pcm_prepare(struct i2s + return -EINVAL; + } + /* we assume all sysclocks are the same! */ +- list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { +- bi.sysclock_factor = cii->codec->sysclock_factor; +- break; +- } ++ bi.sysclock_factor = cii->codec->sysclock_factor; + + if (clock_and_divisors(bi.sysclock_factor, + bi.bus_factor, diff --git a/queue-5.10/alsa-aoa-use-guard-for-mutex-locks.patch b/queue-5.10/alsa-aoa-use-guard-for-mutex-locks.patch new file mode 100644 index 0000000000..ef8f0b5be4 --- /dev/null +++ b/queue-5.10/alsa-aoa-use-guard-for-mutex-locks.patch @@ -0,0 +1,1041 @@ +From stable+bounces-242480-greg=kroah.com@vger.kernel.org Fri May 1 22:58:14 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 13:27:59 -0400 +Subject: ALSA: aoa: Use guard() for mutex locks +To: stable@vger.kernel.org +Cc: Takashi Iwai , Sasha Levin +Message-ID: <20260501172800.3766490-1-sashal@kernel.org> + +From: Takashi Iwai + +[ Upstream commit 1cb6ecbb372002ef9e531c5377e5f60122411e40 ] + +Replace the manual mutex lock/unlock pairs with guard() for code +simplification. + +Only code refactoring, and no behavior change. + +Signed-off-by: Takashi Iwai +Link: https://patch.msgid.link/20250829151335.7342-14-tiwai@suse.de +Stable-dep-of: 5ed060d54915 ("ALSA: aoa: i2sbus: clear stale prepared state") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/aoa/codecs/onyx.c | 104 +++++++++++------------------------- + sound/aoa/codecs/tas.c | 113 +++++++++++++--------------------------- + sound/aoa/core/gpio-feature.c | 20 ++----- + sound/aoa/core/gpio-pmf.c | 26 +++------ + sound/aoa/soundbus/i2sbus/pcm.c | 76 ++++++++------------------ + 5 files changed, 112 insertions(+), 227 deletions(-) + +--- a/sound/aoa/codecs/onyx.c ++++ b/sound/aoa/codecs/onyx.c +@@ -121,10 +121,9 @@ static int onyx_snd_vol_get(struct snd_k + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + s8 l, r; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); + onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); +- mutex_unlock(&onyx->mutex); + + ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT; + ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT; +@@ -145,15 +144,13 @@ static int onyx_snd_vol_put(struct snd_k + ucontrol->value.integer.value[1] > -1 + VOLUME_RANGE_SHIFT) + return -EINVAL; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); + onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); + + if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] && +- r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) { +- mutex_unlock(&onyx->mutex); ++ r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) + return 0; +- } + + onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, + ucontrol->value.integer.value[0] +@@ -161,7 +158,6 @@ static int onyx_snd_vol_put(struct snd_k + onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, + ucontrol->value.integer.value[1] + - VOLUME_RANGE_SHIFT); +- mutex_unlock(&onyx->mutex); + + return 1; + } +@@ -197,9 +193,8 @@ static int onyx_snd_inputgain_get(struct + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 ig; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig); +- mutex_unlock(&onyx->mutex); + + ucontrol->value.integer.value[0] = + (ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT; +@@ -216,14 +211,13 @@ static int onyx_snd_inputgain_put(struct + if (ucontrol->value.integer.value[0] < 3 + INPUTGAIN_RANGE_SHIFT || + ucontrol->value.integer.value[0] > 28 + INPUTGAIN_RANGE_SHIFT) + return -EINVAL; +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); + n = v; + n &= ~ONYX_ADC_PGA_GAIN_MASK; + n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT) + & ONYX_ADC_PGA_GAIN_MASK; + onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n); +- mutex_unlock(&onyx->mutex); + + return n != v; + } +@@ -251,9 +245,8 @@ static int onyx_snd_capture_source_get(s + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + s8 v; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); +- mutex_unlock(&onyx->mutex); + + ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC); + +@@ -264,13 +257,12 @@ static void onyx_set_capture_source(stru + { + s8 v; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); + v &= ~ONYX_ADC_INPUT_MIC; + if (mic) + v |= ONYX_ADC_INPUT_MIC; + onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v); +- mutex_unlock(&onyx->mutex); + } + + static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol, +@@ -311,9 +303,8 @@ static int onyx_snd_mute_get(struct snd_ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 c; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c); +- mutex_unlock(&onyx->mutex); + + ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT); + ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT); +@@ -328,9 +319,9 @@ static int onyx_snd_mute_put(struct snd_ + u8 v = 0, c = 0; + int err = -EBUSY; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + if (onyx->analog_locked) +- goto out_unlock; ++ return -EBUSY; + + onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); + c = v; +@@ -341,9 +332,6 @@ static int onyx_snd_mute_put(struct snd_ + c |= ONYX_MUTE_RIGHT; + err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c); + +- out_unlock: +- mutex_unlock(&onyx->mutex); +- + return !err ? (v != c) : err; + } + +@@ -372,9 +360,8 @@ static int onyx_snd_single_bit_get(struc + u8 address = (pv >> 8) & 0xff; + u8 mask = pv & 0xff; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, address, &c); +- mutex_unlock(&onyx->mutex); + + ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity; + +@@ -393,11 +380,10 @@ static int onyx_snd_single_bit_put(struc + u8 address = (pv >> 8) & 0xff; + u8 mask = pv & 0xff; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + if (spdiflock && onyx->spdif_locked) { + /* even if alsamixer doesn't care.. */ +- err = -EBUSY; +- goto out_unlock; ++ return -EBUSY; + } + onyx_read_register(onyx, address, &v); + c = v; +@@ -406,9 +392,6 @@ static int onyx_snd_single_bit_put(struc + c |= mask; + err = onyx_write_register(onyx, address, c); + +- out_unlock: +- mutex_unlock(&onyx->mutex); +- + return !err ? (v != c) : err; + } + +@@ -489,7 +472,7 @@ static int onyx_spdif_get(struct snd_kco + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 v; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); + ucontrol->value.iec958.status[0] = v & 0x3e; + +@@ -501,7 +484,6 @@ static int onyx_spdif_get(struct snd_kco + + onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); + ucontrol->value.iec958.status[4] = v & 0x0f; +- mutex_unlock(&onyx->mutex); + + return 0; + } +@@ -512,7 +494,7 @@ static int onyx_spdif_put(struct snd_kco + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 v; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); + v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e); + onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v); +@@ -527,7 +509,6 @@ static int onyx_spdif_put(struct snd_kco + onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); + v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f); + onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); +- mutex_unlock(&onyx->mutex); + + return 1; + } +@@ -672,14 +653,13 @@ static int onyx_usable(struct codec_info + struct onyx *onyx = cii->codec_data; + int spdif_enabled, analog_enabled; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); + spdif_enabled = !!(v & ONYX_SPDIF_ENABLE); + onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); + analog_enabled = + (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT)) + != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT); +- mutex_unlock(&onyx->mutex); + + switch (ti->tag) { + case 0: return 1; +@@ -695,9 +675,8 @@ static int onyx_prepare(struct codec_inf + { + u8 v; + struct onyx *onyx = cii->codec_data; +- int err = -EBUSY; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + + #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE + if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) { +@@ -706,10 +685,9 @@ static int onyx_prepare(struct codec_inf + if (onyx_write_register(onyx, + ONYX_REG_DAC_CONTROL, + v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT)) +- goto out_unlock; ++ return -EBUSY; + onyx->analog_locked = 1; +- err = 0; +- goto out_unlock; ++ return 0; + } + #endif + switch (substream->runtime->rate) { +@@ -719,8 +697,7 @@ static int onyx_prepare(struct codec_inf + /* these rates are ok for all outputs */ + /* FIXME: program spdif channel control bits here so that + * userspace doesn't have to if it only plays pcm! */ +- err = 0; +- goto out_unlock; ++ return 0; + default: + /* got some rate that the digital output can't do, + * so disable and lock it */ +@@ -728,16 +705,12 @@ static int onyx_prepare(struct codec_inf + if (onyx_write_register(onyx, + ONYX_REG_DIG_INFO4, + v & ~ONYX_SPDIF_ENABLE)) +- goto out_unlock; ++ return -EBUSY; + onyx->spdif_locked = 1; +- err = 0; +- goto out_unlock; ++ return 0; + } + +- out_unlock: +- mutex_unlock(&onyx->mutex); +- +- return err; ++ return -EBUSY; + } + + static int onyx_open(struct codec_info_item *cii, +@@ -745,9 +718,8 @@ static int onyx_open(struct codec_info_i + { + struct onyx *onyx = cii->codec_data; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx->open_count++; +- mutex_unlock(&onyx->mutex); + + return 0; + } +@@ -757,11 +729,10 @@ static int onyx_close(struct codec_info_ + { + struct onyx *onyx = cii->codec_data; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + onyx->open_count--; + if (!onyx->open_count) + onyx->spdif_locked = onyx->analog_locked = 0; +- mutex_unlock(&onyx->mutex); + + return 0; + } +@@ -771,7 +742,7 @@ static int onyx_switch_clock(struct code + { + struct onyx *onyx = cii->codec_data; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + /* this *MUST* be more elaborate later... */ + switch (what) { + case CLOCK_SWITCH_PREPARE_SLAVE: +@@ -783,7 +754,6 @@ static int onyx_switch_clock(struct code + default: /* silence warning */ + break; + } +- mutex_unlock(&onyx->mutex); + + return 0; + } +@@ -794,27 +764,21 @@ static int onyx_suspend(struct codec_inf + { + struct onyx *onyx = cii->codec_data; + u8 v; +- int err = -ENXIO; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) +- goto out_unlock; ++ return -ENXIO; + onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV); + /* Apple does a sleep here but the datasheet says to do it on resume */ +- err = 0; +- out_unlock: +- mutex_unlock(&onyx->mutex); +- +- return err; ++ return 0; + } + + static int onyx_resume(struct codec_info_item *cii) + { + struct onyx *onyx = cii->codec_data; + u8 v; +- int err = -ENXIO; + +- mutex_lock(&onyx->mutex); ++ guard(mutex)(&onyx->mutex); + + /* reset codec */ + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); +@@ -826,17 +790,13 @@ static int onyx_resume(struct codec_info + + /* take codec out of suspend (if it still is after reset) */ + if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) +- goto out_unlock; ++ return -ENXIO; + onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV)); + /* FIXME: should divide by sample rate, but 8k is the lowest we go */ + msleep(2205000/8000); + /* reset all values */ + onyx_register_init(onyx); +- err = 0; +- out_unlock: +- mutex_unlock(&onyx->mutex); +- +- return err; ++ return 0; + } + + #endif /* CONFIG_PM */ +--- a/sound/aoa/codecs/tas.c ++++ b/sound/aoa/codecs/tas.c +@@ -236,10 +236,9 @@ static int tas_snd_vol_get(struct snd_kc + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + ucontrol->value.integer.value[0] = tas->cached_volume_l; + ucontrol->value.integer.value[1] = tas->cached_volume_r; +- mutex_unlock(&tas->mtx); + return 0; + } + +@@ -255,18 +254,15 @@ static int tas_snd_vol_put(struct snd_kc + ucontrol->value.integer.value[1] > 177) + return -EINVAL; + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + if (tas->cached_volume_l == ucontrol->value.integer.value[0] +- && tas->cached_volume_r == ucontrol->value.integer.value[1]) { +- mutex_unlock(&tas->mtx); ++ && tas->cached_volume_r == ucontrol->value.integer.value[1]) + return 0; +- } + + tas->cached_volume_l = ucontrol->value.integer.value[0]; + tas->cached_volume_r = ucontrol->value.integer.value[1]; + if (tas->hw_enabled) + tas_set_volume(tas); +- mutex_unlock(&tas->mtx); + return 1; + } + +@@ -286,10 +282,9 @@ static int tas_snd_mute_get(struct snd_k + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + ucontrol->value.integer.value[0] = !tas->mute_l; + ucontrol->value.integer.value[1] = !tas->mute_r; +- mutex_unlock(&tas->mtx); + return 0; + } + +@@ -298,18 +293,15 @@ static int tas_snd_mute_put(struct snd_k + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + if (tas->mute_l == !ucontrol->value.integer.value[0] +- && tas->mute_r == !ucontrol->value.integer.value[1]) { +- mutex_unlock(&tas->mtx); ++ && tas->mute_r == !ucontrol->value.integer.value[1]) + return 0; +- } + + tas->mute_l = !ucontrol->value.integer.value[0]; + tas->mute_r = !ucontrol->value.integer.value[1]; + if (tas->hw_enabled) + tas_set_volume(tas); +- mutex_unlock(&tas->mtx); + return 1; + } + +@@ -338,10 +330,9 @@ static int tas_snd_mixer_get(struct snd_ + struct tas *tas = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + ucontrol->value.integer.value[0] = tas->mixer_l[idx]; + ucontrol->value.integer.value[1] = tas->mixer_r[idx]; +- mutex_unlock(&tas->mtx); + + return 0; + } +@@ -352,19 +343,16 @@ static int tas_snd_mixer_put(struct snd_ + struct tas *tas = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + if (tas->mixer_l[idx] == ucontrol->value.integer.value[0] +- && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) { +- mutex_unlock(&tas->mtx); ++ && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) + return 0; +- } + + tas->mixer_l[idx] = ucontrol->value.integer.value[0]; + tas->mixer_r[idx] = ucontrol->value.integer.value[1]; + + if (tas->hw_enabled) + tas_set_mixer(tas); +- mutex_unlock(&tas->mtx); + return 1; + } + +@@ -397,9 +385,8 @@ static int tas_snd_drc_range_get(struct + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + ucontrol->value.integer.value[0] = tas->drc_range; +- mutex_unlock(&tas->mtx); + return 0; + } + +@@ -412,16 +399,13 @@ static int tas_snd_drc_range_put(struct + ucontrol->value.integer.value[0] > TAS3004_DRC_MAX) + return -EINVAL; + +- mutex_lock(&tas->mtx); +- if (tas->drc_range == ucontrol->value.integer.value[0]) { +- mutex_unlock(&tas->mtx); ++ guard(mutex)(&tas->mtx); ++ if (tas->drc_range == ucontrol->value.integer.value[0]) + return 0; +- } + + tas->drc_range = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas3004_set_drc(tas); +- mutex_unlock(&tas->mtx); + return 1; + } + +@@ -441,9 +425,8 @@ static int tas_snd_drc_switch_get(struct + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + ucontrol->value.integer.value[0] = tas->drc_enabled; +- mutex_unlock(&tas->mtx); + return 0; + } + +@@ -452,16 +435,13 @@ static int tas_snd_drc_switch_put(struct + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); +- if (tas->drc_enabled == ucontrol->value.integer.value[0]) { +- mutex_unlock(&tas->mtx); ++ guard(mutex)(&tas->mtx); ++ if (tas->drc_enabled == ucontrol->value.integer.value[0]) + return 0; +- } + + tas->drc_enabled = !!ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas3004_set_drc(tas); +- mutex_unlock(&tas->mtx); + return 1; + } + +@@ -487,9 +467,8 @@ static int tas_snd_capture_source_get(st + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B); +- mutex_unlock(&tas->mtx); + return 0; + } + +@@ -501,7 +480,7 @@ static int tas_snd_capture_source_put(st + + if (ucontrol->value.enumerated.item[0] > 1) + return -EINVAL; +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + oldacr = tas->acr; + + /* +@@ -513,13 +492,10 @@ static int tas_snd_capture_source_put(st + if (ucontrol->value.enumerated.item[0]) + tas->acr |= TAS_ACR_INPUT_B | TAS_ACR_B_MONAUREAL | + TAS_ACR_B_MON_SEL_RIGHT; +- if (oldacr == tas->acr) { +- mutex_unlock(&tas->mtx); ++ if (oldacr == tas->acr) + return 0; +- } + if (tas->hw_enabled) + tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); +- mutex_unlock(&tas->mtx); + return 1; + } + +@@ -558,9 +534,8 @@ static int tas_snd_treble_get(struct snd + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + ucontrol->value.integer.value[0] = tas->treble; +- mutex_unlock(&tas->mtx); + return 0; + } + +@@ -572,16 +547,13 @@ static int tas_snd_treble_put(struct snd + if (ucontrol->value.integer.value[0] < TAS3004_TREBLE_MIN || + ucontrol->value.integer.value[0] > TAS3004_TREBLE_MAX) + return -EINVAL; +- mutex_lock(&tas->mtx); +- if (tas->treble == ucontrol->value.integer.value[0]) { +- mutex_unlock(&tas->mtx); ++ guard(mutex)(&tas->mtx); ++ if (tas->treble == ucontrol->value.integer.value[0]) + return 0; +- } + + tas->treble = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas_set_treble(tas); +- mutex_unlock(&tas->mtx); + return 1; + } + +@@ -609,9 +581,8 @@ static int tas_snd_bass_get(struct snd_k + { + struct tas *tas = snd_kcontrol_chip(kcontrol); + +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + ucontrol->value.integer.value[0] = tas->bass; +- mutex_unlock(&tas->mtx); + return 0; + } + +@@ -623,16 +594,13 @@ static int tas_snd_bass_put(struct snd_k + if (ucontrol->value.integer.value[0] < TAS3004_BASS_MIN || + ucontrol->value.integer.value[0] > TAS3004_BASS_MAX) + return -EINVAL; +- mutex_lock(&tas->mtx); +- if (tas->bass == ucontrol->value.integer.value[0]) { +- mutex_unlock(&tas->mtx); ++ guard(mutex)(&tas->mtx); ++ if (tas->bass == ucontrol->value.integer.value[0]) + return 0; +- } + + tas->bass = ucontrol->value.integer.value[0]; + if (tas->hw_enabled) + tas_set_bass(tas); +- mutex_unlock(&tas->mtx); + return 1; + } + +@@ -723,13 +691,13 @@ static int tas_switch_clock(struct codec + break; + case CLOCK_SWITCH_SLAVE: + /* Clocks are back, re-init the codec */ +- mutex_lock(&tas->mtx); +- tas_reset_init(tas); +- tas_set_volume(tas); +- tas_set_mixer(tas); +- tas->hw_enabled = 1; +- tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio); +- mutex_unlock(&tas->mtx); ++ scoped_guard(mutex, &tas->mtx) { ++ tas_reset_init(tas); ++ tas_set_volume(tas); ++ tas_set_mixer(tas); ++ tas->hw_enabled = 1; ++ tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio); ++ } + break; + default: + /* doesn't happen as of now */ +@@ -744,23 +712,21 @@ static int tas_switch_clock(struct codec + * our i2c device is suspended, and then take note of that! */ + static int tas_suspend(struct tas *tas) + { +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + tas->hw_enabled = 0; + tas->acr |= TAS_ACR_ANALOG_PDOWN; + tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); +- mutex_unlock(&tas->mtx); + return 0; + } + + static int tas_resume(struct tas *tas) + { + /* reset codec */ +- mutex_lock(&tas->mtx); ++ guard(mutex)(&tas->mtx); + tas_reset_init(tas); + tas_set_volume(tas); + tas_set_mixer(tas); + tas->hw_enabled = 1; +- mutex_unlock(&tas->mtx); + return 0; + } + +@@ -803,14 +769,13 @@ static int tas_init_codec(struct aoa_cod + return -EINVAL; + } + +- mutex_lock(&tas->mtx); +- if (tas_reset_init(tas)) { +- printk(KERN_ERR PFX "tas failed to initialise\n"); +- mutex_unlock(&tas->mtx); +- return -ENXIO; ++ scoped_guard(mutex, &tas->mtx) { ++ if (tas_reset_init(tas)) { ++ printk(KERN_ERR PFX "tas failed to initialise\n"); ++ return -ENXIO; ++ } ++ tas->hw_enabled = 1; + } +- tas->hw_enabled = 1; +- mutex_unlock(&tas->mtx); + + if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev, + aoa_get_card(), +--- a/sound/aoa/core/gpio-feature.c ++++ b/sound/aoa/core/gpio-feature.c +@@ -212,10 +212,9 @@ static void ftr_handle_notify(struct wor + struct gpio_notification *notif = + container_of(work, struct gpio_notification, work.work); + +- mutex_lock(¬if->mutex); ++ guard(mutex)(¬if->mutex); + if (notif->notify) + notif->notify(notif->data); +- mutex_unlock(¬if->mutex); + } + + static void gpio_enable_dual_edge(int gpio) +@@ -341,19 +340,17 @@ static int ftr_set_notify(struct gpio_ru + if (!irq) + return -ENODEV; + +- mutex_lock(¬if->mutex); ++ guard(mutex)(¬if->mutex); + + old = notif->notify; + +- if (!old && !notify) { +- err = 0; +- goto out_unlock; +- } ++ if (!old && !notify) ++ return 0; + + if (old && notify) { + if (old == notify && notif->data == data) + err = 0; +- goto out_unlock; ++ return err; + } + + if (old && !notify) +@@ -362,16 +359,13 @@ static int ftr_set_notify(struct gpio_ru + if (!old && notify) { + err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif); + if (err) +- goto out_unlock; ++ return err; + } + + notif->notify = notify; + notif->data = data; + +- err = 0; +- out_unlock: +- mutex_unlock(¬if->mutex); +- return err; ++ return 0; + } + + static int ftr_get_detect(struct gpio_runtime *rt, +--- a/sound/aoa/core/gpio-pmf.c ++++ b/sound/aoa/core/gpio-pmf.c +@@ -74,10 +74,9 @@ static void pmf_handle_notify(struct wor + struct gpio_notification *notif = + container_of(work, struct gpio_notification, work.work); + +- mutex_lock(¬if->mutex); ++ guard(mutex)(¬if->mutex); + if (notif->notify) + notif->notify(notif->data); +- mutex_unlock(¬if->mutex); + } + + static void pmf_gpio_init(struct gpio_runtime *rt) +@@ -154,19 +153,17 @@ static int pmf_set_notify(struct gpio_ru + return -EINVAL; + } + +- mutex_lock(¬if->mutex); ++ guard(mutex)(¬if->mutex); + + old = notif->notify; + +- if (!old && !notify) { +- err = 0; +- goto out_unlock; +- } ++ if (!old && !notify) ++ return 0; + + if (old && notify) { + if (old == notify && notif->data == data) + err = 0; +- goto out_unlock; ++ return err; + } + + if (old && !notify) { +@@ -178,10 +175,8 @@ static int pmf_set_notify(struct gpio_ru + if (!old && notify) { + irq_client = kzalloc(sizeof(struct pmf_irq_client), + GFP_KERNEL); +- if (!irq_client) { +- err = -ENOMEM; +- goto out_unlock; +- } ++ if (!irq_client) ++ return -ENOMEM; + irq_client->data = notif; + irq_client->handler = pmf_handle_notify_irq; + irq_client->owner = THIS_MODULE; +@@ -192,17 +187,14 @@ static int pmf_set_notify(struct gpio_ru + printk(KERN_ERR "snd-aoa: gpio layer failed to" + " register %s irq (%d)\n", name, err); + kfree(irq_client); +- goto out_unlock; ++ return err; + } + notif->gpio_private = irq_client; + } + notif->notify = notify; + notif->data = data; + +- err = 0; +- out_unlock: +- mutex_unlock(¬if->mutex); +- return err; ++ return 0; + } + + static int pmf_get_detect(struct gpio_runtime *rt, +--- a/sound/aoa/soundbus/i2sbus/pcm.c ++++ b/sound/aoa/soundbus/i2sbus/pcm.c +@@ -79,11 +79,10 @@ static int i2sbus_pcm_open(struct i2sbus + u64 formats = 0; + unsigned int rates = 0; + struct transfer_info v; +- int result = 0; + int bus_factor = 0, sysclock_factor = 0; + int found_this; + +- mutex_lock(&i2sdev->lock); ++ guard(mutex)(&i2sdev->lock); + + get_pcm_info(i2sdev, in, &pi, &other); + +@@ -92,8 +91,7 @@ static int i2sbus_pcm_open(struct i2sbus + + if (pi->active) { + /* alsa messed up */ +- result = -EBUSY; +- goto out_unlock; ++ return -EBUSY; + } + + /* we now need to assign the hw */ +@@ -117,10 +115,8 @@ static int i2sbus_pcm_open(struct i2sbus + ti++; + } + } +- if (!masks_inited || !bus_factor || !sysclock_factor) { +- result = -ENODEV; +- goto out_unlock; +- } ++ if (!masks_inited || !bus_factor || !sysclock_factor) ++ return -ENODEV; + /* bus dependent stuff */ + hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | +@@ -194,15 +190,12 @@ static int i2sbus_pcm_open(struct i2sbus + hw->periods_max = MAX_DBDMA_COMMANDS; + err = snd_pcm_hw_constraint_integer(pi->substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); +- if (err < 0) { +- result = err; +- goto out_unlock; +- } ++ if (err < 0) ++ return err; + list_for_each_entry(cii, &sdev->codec_list, list) { + if (cii->codec->open) { + err = cii->codec->open(cii, pi->substream); + if (err) { +- result = err; + /* unwind */ + found_this = 0; + list_for_each_entry_reverse(rev, +@@ -214,14 +207,12 @@ static int i2sbus_pcm_open(struct i2sbus + if (rev == cii) + found_this = 1; + } +- goto out_unlock; ++ return err; + } + } + } + +- out_unlock: +- mutex_unlock(&i2sdev->lock); +- return result; ++ return 0; + } + + #undef CHECK_RATE +@@ -232,7 +223,7 @@ static int i2sbus_pcm_close(struct i2sbu + struct pcm_info *pi; + int err = 0, tmp; + +- mutex_lock(&i2sdev->lock); ++ guard(mutex)(&i2sdev->lock); + + get_pcm_info(i2sdev, in, &pi, NULL); + +@@ -246,7 +237,6 @@ static int i2sbus_pcm_close(struct i2sbu + + pi->substream = NULL; + pi->active = 0; +- mutex_unlock(&i2sdev->lock); + return err; + } + +@@ -330,33 +320,26 @@ static int i2sbus_pcm_prepare(struct i2s + int input_16bit; + struct pcm_info *pi, *other; + int cnt; +- int result = 0; + unsigned int cmd, stopaddr; + +- mutex_lock(&i2sdev->lock); ++ guard(mutex)(&i2sdev->lock); + + get_pcm_info(i2sdev, in, &pi, &other); + +- if (pi->dbdma_ring.running) { +- result = -EBUSY; +- goto out_unlock; +- } ++ if (pi->dbdma_ring.running) ++ return -EBUSY; + if (pi->dbdma_ring.stopping) + i2sbus_wait_for_stop(i2sdev, pi); + +- if (!pi->substream || !pi->substream->runtime) { +- result = -EINVAL; +- goto out_unlock; +- } ++ if (!pi->substream || !pi->substream->runtime) ++ return -EINVAL; + + runtime = pi->substream->runtime; + pi->active = 1; + if (other->active && + ((i2sdev->format != runtime->format) +- || (i2sdev->rate != runtime->rate))) { +- result = -EINVAL; +- goto out_unlock; +- } ++ || (i2sdev->rate != runtime->rate))) ++ return -EINVAL; + + i2sdev->format = runtime->format; + i2sdev->rate = runtime->rate; +@@ -412,10 +395,8 @@ static int i2sbus_pcm_prepare(struct i2s + bi.bus_factor = cii->codec->bus_factor; + break; + } +- if (!bi.bus_factor) { +- result = -ENODEV; +- goto out_unlock; +- } ++ if (!bi.bus_factor) ++ return -ENODEV; + input_16bit = 1; + break; + case SNDRV_PCM_FORMAT_S32_BE: +@@ -426,8 +407,7 @@ static int i2sbus_pcm_prepare(struct i2s + input_16bit = 0; + break; + default: +- result = -EINVAL; +- goto out_unlock; ++ return -EINVAL; + } + /* we assume all sysclocks are the same! */ + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { +@@ -438,10 +418,8 @@ static int i2sbus_pcm_prepare(struct i2s + if (clock_and_divisors(bi.sysclock_factor, + bi.bus_factor, + runtime->rate, +- &sfr) < 0) { +- result = -EINVAL; +- goto out_unlock; +- } ++ &sfr) < 0) ++ return -EINVAL; + switch (bi.bus_factor) { + case 32: + sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X; +@@ -457,10 +435,8 @@ static int i2sbus_pcm_prepare(struct i2s + int err = 0; + if (cii->codec->prepare) + err = cii->codec->prepare(cii, &bi, pi->substream); +- if (err) { +- result = err; +- goto out_unlock; +- } ++ if (err) ++ return err; + } + /* codecs are fine with it, so set our clocks */ + if (input_16bit) +@@ -476,7 +452,7 @@ static int i2sbus_pcm_prepare(struct i2s + /* not locking these is fine since we touch them only in this function */ + if (in_le32(&i2sdev->intfregs->serial_format) == sfr + && in_le32(&i2sdev->intfregs->data_word_sizes) == dws) +- goto out_unlock; ++ return 0; + + /* let's notify the codecs about clocks going away. + * For now we only do mastering on the i2s cell... */ +@@ -514,9 +490,7 @@ static int i2sbus_pcm_prepare(struct i2s + if (cii->codec->switch_clock) + cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE); + +- out_unlock: +- mutex_unlock(&i2sdev->lock); +- return result; ++ return 0; + } + + #ifdef CONFIG_PM diff --git a/queue-5.10/alsa-core-fix-potential-data-race-at-fasync-handling.patch b/queue-5.10/alsa-core-fix-potential-data-race-at-fasync-handling.patch new file mode 100644 index 0000000000..8a9ace6634 --- /dev/null +++ b/queue-5.10/alsa-core-fix-potential-data-race-at-fasync-handling.patch @@ -0,0 +1,76 @@ +From stable+bounces-242655-greg=kroah.com@vger.kernel.org Sun May 3 16:33:57 2026 +From: Sasha Levin +Date: Sun, 3 May 2026 07:03:44 -0400 +Subject: ALSA: core: Fix potential data race at fasync handling +To: stable@vger.kernel.org +Cc: Takashi Iwai , Jake Lamberson , Sasha Levin +Message-ID: <20260503110344.1034091-1-sashal@kernel.org> + +From: Takashi Iwai + +[ Upstream commit 8146cd333d235ed32d48bb803fdf743472d7c783 ] + +In snd_fasync_work_fn(), which is the offload work for traversing and +processing the pending fasync list, the call of kill_fasync() is done +outside the snd_fasync_lock for avoiding deadlocks. The problem is +that its the references of fasync->on, fasync->signal and fasync->poll +are done there also outside the lock. Since these may be modified by +snd_kill_fasync() call concurrently from other process, inconsistent +values might be passed to kill_fasync(). Although there shouldn't be +critical UAF, it's still better to be addressed. + +This patch moves the kill_fasync() argument evaluations inside the +snd_fasync_lock for avoiding the data races above. The handling in +fasync->on flag is optimized in the loop to skip directly. + +Also, for more clarity, snd_fasync_free() takes the lock and unlink +the pending entry more directly instead of clearing fasync->on flag. + +Reported-by: Jake Lamberson +Fixes: ef34a0ae7a26 ("ALSA: core: Add async signal helpers") +Cc: +Link: https://patch.msgid.link/20260420061721.3253644-1-tiwai@suse.de +Signed-off-by: Takashi Iwai +[ replaced scoped_guard(spinlock_irq, &snd_fasync_lock) with explicit spin_lock_irq()/spin_unlock_irq() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/core/misc.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +--- a/sound/core/misc.c ++++ b/sound/core/misc.c +@@ -171,14 +171,18 @@ static LIST_HEAD(snd_fasync_list); + static void snd_fasync_work_fn(struct work_struct *work) + { + struct snd_fasync *fasync; ++ int signal, poll; + + spin_lock_irq(&snd_fasync_lock); + while (!list_empty(&snd_fasync_list)) { + fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list); + list_del_init(&fasync->list); ++ if (!fasync->on) ++ continue; ++ signal = fasync->signal; ++ poll = fasync->poll; + spin_unlock_irq(&snd_fasync_lock); +- if (fasync->on) +- kill_fasync(&fasync->fasync, fasync->signal, fasync->poll); ++ kill_fasync(&fasync->fasync, signal, poll); + spin_lock_irq(&snd_fasync_lock); + } + spin_unlock_irq(&snd_fasync_lock); +@@ -234,7 +238,11 @@ void snd_fasync_free(struct snd_fasync * + { + if (!fasync) + return; +- fasync->on = 0; ++ ++ spin_lock_irq(&snd_fasync_lock); ++ list_del_init(&fasync->list); ++ spin_unlock_irq(&snd_fasync_lock); ++ + flush_work(&snd_fasync_work); + kfree(fasync); + } diff --git a/queue-5.10/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch b/queue-5.10/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch new file mode 100644 index 0000000000..9ac9ac6c3a --- /dev/null +++ b/queue-5.10/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch @@ -0,0 +1,137 @@ +From stable+bounces-241768-greg=kroah.com@vger.kernel.org Wed Apr 29 01:35:55 2026 +From: Sasha Levin +Date: Tue, 28 Apr 2026 16:05:48 -0400 +Subject: arm64/mm: Enable batched TLB flush in unmap_hotplug_range() +To: stable@vger.kernel.org +Cc: Anshuman Khandual , Will Deacon , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, "David Hildenbrand (Arm)" , Ryan Roberts , Catalin Marinas , Sasha Levin +Message-ID: <20260428200548.3191346-1-sashal@kernel.org> + +From: Anshuman Khandual + +[ Upstream commit 48478b9f791376b4b89018d7afdfd06865498f65 ] + +During a memory hot remove operation, both linear and vmemmap mappings for +the memory range being removed, get unmapped via unmap_hotplug_range() but +mapped pages get freed only for vmemmap mapping. This is just a sequential +operation where each table entry gets cleared, followed by a leaf specific +TLB flush, and then followed by memory free operation when applicable. + +This approach was simple and uniform both for vmemmap and linear mappings. +But linear mapping might contain CONT marked block memory where it becomes +necessary to first clear out all entire in the range before a TLB flush. +This is as per the architecture requirement. Hence batch all TLB flushes +during the table tear down walk and finally do it in unmap_hotplug_range(). + +Prior to this fix, it was hypothetically possible for a speculative access +to a higher address in the contiguous block to fill the TLB with shattered +entries for the entire contiguous range after a lower address had already +been cleared and invalidated. Due to the table entries being shattered, the +subsequent TLB invalidation for the higher address would not then clear the +TLB entries for the lower address, meaning stale TLB entries could persist. + +Besides it also helps in improving the performance via TLBI range operation +along with reduced synchronization instructions. The time spent executing +unmap_hotplug_range() improved 97% measured over a 2GB memory hot removal +in KVM guest. + +This scheme is not applicable during vmemmap mapping tear down where memory +needs to be freed and hence a TLB flush is required after clearing out page +table entry. + +Cc: Will Deacon +Cc: linux-arm-kernel@lists.infradead.org +Cc: linux-kernel@vger.kernel.org +Closes: https://lore.kernel.org/all/aWZYXhrT6D2M-7-N@willie-the-truck/ +Fixes: bbd6ec605c0f ("arm64/mm: Enable memory hot remove") +Cc: stable@vger.kernel.org +Reviewed-by: David Hildenbrand (Arm) +Reviewed-by: Ryan Roberts +Signed-off-by: Ryan Roberts +Signed-off-by: Anshuman Khandual +Signed-off-by: Catalin Marinas +[ renamed `__pte_clear()` to `pte_clear()` and inlined `pmd_cont(pmd)` as `pmd_val(pmd) & PMD_SECT_CONT` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/mm/mmu.c | 36 ++++++++++++++++++++---------------- + 1 file changed, 20 insertions(+), 16 deletions(-) + +--- a/arch/arm64/mm/mmu.c ++++ b/arch/arm64/mm/mmu.c +@@ -862,10 +862,14 @@ static void unmap_hotplug_pte_range(pmd_ + + WARN_ON(!pte_present(pte)); + pte_clear(&init_mm, addr, ptep); +- flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +- if (free_mapped) ++ if (free_mapped) { ++ /* CONT blocks are not supported in the vmemmap */ ++ WARN_ON(pte_cont(pte)); ++ flush_tlb_kernel_range(addr, addr + PAGE_SIZE); + free_hotplug_page_range(pte_page(pte), + PAGE_SIZE, altmap); ++ } ++ /* unmap_hotplug_range() flushes TLB for !free_mapped */ + } while (addr += PAGE_SIZE, addr < end); + } + +@@ -886,15 +890,14 @@ static void unmap_hotplug_pmd_range(pud_ + WARN_ON(!pmd_present(pmd)); + if (pmd_sect(pmd)) { + pmd_clear(pmdp); +- +- /* +- * One TLBI should be sufficient here as the PMD_SIZE +- * range is mapped with a single block entry. +- */ +- flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +- if (free_mapped) ++ if (free_mapped) { ++ /* CONT blocks are not supported in the vmemmap */ ++ WARN_ON(pmd_val(pmd) & PMD_SECT_CONT); ++ flush_tlb_kernel_range(addr, addr + PMD_SIZE); + free_hotplug_page_range(pmd_page(pmd), + PMD_SIZE, altmap); ++ } ++ /* unmap_hotplug_range() flushes TLB for !free_mapped */ + continue; + } + WARN_ON(!pmd_table(pmd)); +@@ -919,15 +922,12 @@ static void unmap_hotplug_pud_range(p4d_ + WARN_ON(!pud_present(pud)); + if (pud_sect(pud)) { + pud_clear(pudp); +- +- /* +- * One TLBI should be sufficient here as the PUD_SIZE +- * range is mapped with a single block entry. +- */ +- flush_tlb_kernel_range(addr, addr + PAGE_SIZE); +- if (free_mapped) ++ if (free_mapped) { ++ flush_tlb_kernel_range(addr, addr + PUD_SIZE); + free_hotplug_page_range(pud_page(pud), + PUD_SIZE, altmap); ++ } ++ /* unmap_hotplug_range() flushes TLB for !free_mapped */ + continue; + } + WARN_ON(!pud_table(pud)); +@@ -957,6 +957,7 @@ static void unmap_hotplug_p4d_range(pgd_ + static void unmap_hotplug_range(unsigned long addr, unsigned long end, + bool free_mapped, struct vmem_altmap *altmap) + { ++ unsigned long start = addr; + unsigned long next; + pgd_t *pgdp, pgd; + +@@ -978,6 +979,9 @@ static void unmap_hotplug_range(unsigned + WARN_ON(!pgd_present(pgd)); + unmap_hotplug_p4d_range(pgdp, addr, next, free_mapped, altmap); + } while (addr = next, addr < end); ++ ++ if (!free_mapped) ++ flush_tlb_kernel_range(start, end); + } + + static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr, diff --git a/queue-5.10/arm64-tlb-flush-walk-cache-when-unsharing-pmd-tables.patch b/queue-5.10/arm64-tlb-flush-walk-cache-when-unsharing-pmd-tables.patch new file mode 100644 index 0000000000..00bd0abb20 --- /dev/null +++ b/queue-5.10/arm64-tlb-flush-walk-cache-when-unsharing-pmd-tables.patch @@ -0,0 +1,55 @@ +From stable+bounces-259527-greg=kroah.com@vger.kernel.org Mon Jun 1 16:36:58 2026 +From: Sasha Levin +Date: Mon, 1 Jun 2026 07:02:09 -0400 +Subject: arm64: tlb: Flush walk cache when unsharing PMD tables +To: stable@vger.kernel.org +Cc: Zeng Heng , Catalin Marinas , Sasha Levin +Message-ID: <20260601110209.439741-1-sashal@kernel.org> + +From: Zeng Heng + +[ Upstream commit c2ff4764e03e7a8d758352f4aceb8fe1be6ac971 ] + +When huge_pmd_unshare() is called to unshare a PMD table, the +tlb_unshare_pmd_ptdesc() function sets tlb->unshared_tables=true +but the aarch64 tlb_flush() only checked tlb->freed_tables to +determine whether to use TLBF_NONE (vae1is, invalidates walk +cache) or TLBF_NOWALKCACHE (vale1is, leaf-only). + +This caused the stale PMD page table entry to remain in the walk cache +after unshare, potentially leading to incorrect page table walks. + +Fix by including unshared_tables in the check, so that when +unsharing tables, TLBF_NONE is used and the walk cache is properly +invalidated. + +Here is the detailed distinction between vae1is and vale1is: + +| Instruction Combination | Actual Invalidation Scope | +| ------------------------ | --------------------------------------------------| +| `VAE1IS` + TTL=`0` | All entries at all levels (full invalidation) | +| `VAE1IS` + TTL=`2` (L2) | Non-leaf at Level 0/1 + leaf at Level 2 | +| `VALE1IS` + TTL=`0` | Leaf entries at all levels (non-leaf not cleared) | +| `VALE1IS` + TTL=`2` (L2) | Leaf entry at Level 2 only | + +Signed-off-by: Zeng Heng +Fixes: 8ce720d5bd91 ("mm/hugetlb: fix excessive IPI broadcasts when unsharing PMD tables using mmu_gather") +Cc: +Signed-off-by: Catalin Marinas +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + arch/arm64/include/asm/tlb.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/arm64/include/asm/tlb.h ++++ b/arch/arm64/include/asm/tlb.h +@@ -53,7 +53,7 @@ static inline int tlb_get_level(struct m + static inline void tlb_flush(struct mmu_gather *tlb) + { + struct vm_area_struct vma = TLB_FLUSH_VMA(tlb->mm, 0); +- bool last_level = !tlb->freed_tables; ++ bool last_level = !(tlb->freed_tables || tlb->unshared_tables); + unsigned long stride = tlb_get_unmap_size(tlb); + int tlb_level = tlb_get_level(tlb); + diff --git a/queue-5.10/bluetooth-consolidate-code-around-sk_alloc-into-a-helper-function.patch b/queue-5.10/bluetooth-consolidate-code-around-sk_alloc-into-a-helper-function.patch new file mode 100644 index 0000000000..bcd651ed82 --- /dev/null +++ b/queue-5.10/bluetooth-consolidate-code-around-sk_alloc-into-a-helper-function.patch @@ -0,0 +1,204 @@ +From stable+bounces-256833-greg=kroah.com@vger.kernel.org Sat May 30 05:23:40 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 19:53:21 -0400 +Subject: Bluetooth: Consolidate code around sk_alloc into a helper function +To: stable@vger.kernel.org +Cc: Luiz Augusto von Dentz , Sasha Levin +Message-ID: <20260529235323.1964735-1-sashal@kernel.org> + +From: Luiz Augusto von Dentz + +[ Upstream commit 6bfa273e533d7b25eee3d74e28a7fe8e6a8e7a93 ] + +This consolidates code around sk_alloc into bt_sock_alloc which does +take care of common initialization. + +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: e83f5e24da74 ("Bluetooth: serialize accept_q access") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/net/bluetooth/bluetooth.h | 2 ++ + net/bluetooth/af_bluetooth.c | 21 +++++++++++++++++++++ + net/bluetooth/bnep/sock.c | 10 +--------- + net/bluetooth/hci_sock.c | 10 ++-------- + net/bluetooth/l2cap_sock.c | 10 +--------- + net/bluetooth/rfcomm/sock.c | 13 +++---------- + net/bluetooth/sco.c | 10 +--------- + 7 files changed, 31 insertions(+), 45 deletions(-) + +--- a/include/net/bluetooth/bluetooth.h ++++ b/include/net/bluetooth/bluetooth.h +@@ -314,6 +314,8 @@ int bt_sock_register(int proto, const s + void bt_sock_unregister(int proto); + void bt_sock_link(struct bt_sock_list *l, struct sock *s); + void bt_sock_unlink(struct bt_sock_list *l, struct sock *s); ++struct sock *bt_sock_alloc(struct net *net, struct socket *sock, ++ struct proto *prot, int proto, gfp_t prio, int kern); + int bt_sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + int flags); + int bt_sock_stream_recvmsg(struct socket *sock, struct msghdr *msg, +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -138,6 +138,27 @@ static int bt_sock_create(struct net *ne + return err; + } + ++struct sock *bt_sock_alloc(struct net *net, struct socket *sock, ++ struct proto *prot, int proto, gfp_t prio, int kern) ++{ ++ struct sock *sk; ++ ++ sk = sk_alloc(net, PF_BLUETOOTH, prio, prot, kern); ++ if (!sk) ++ return NULL; ++ ++ sock_init_data(sock, sk); ++ INIT_LIST_HEAD(&bt_sk(sk)->accept_q); ++ ++ sock_reset_flag(sk, SOCK_ZAPPED); ++ ++ sk->sk_protocol = proto; ++ sk->sk_state = BT_OPEN; ++ ++ return sk; ++} ++EXPORT_SYMBOL(bt_sock_alloc); ++ + void bt_sock_link(struct bt_sock_list *l, struct sock *sk) + { + write_lock(&l->lock); +--- a/net/bluetooth/bnep/sock.c ++++ b/net/bluetooth/bnep/sock.c +@@ -204,21 +204,13 @@ static int bnep_sock_create(struct net * + if (sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + +- sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &bnep_proto, kern); ++ sk = bt_sock_alloc(net, sock, &bnep_proto, protocol, GFP_ATOMIC, kern); + if (!sk) + return -ENOMEM; + +- sock_init_data(sock, sk); +- + sock->ops = &bnep_sock_ops; +- + sock->state = SS_UNCONNECTED; + +- sock_reset_flag(sk, SOCK_ZAPPED); +- +- sk->sk_protocol = protocol; +- sk->sk_state = BT_OPEN; +- + bt_sock_link(&bnep_sk_list, sk); + return 0; + } +--- a/net/bluetooth/hci_sock.c ++++ b/net/bluetooth/hci_sock.c +@@ -2091,18 +2091,12 @@ static int hci_sock_create(struct net *n + + sock->ops = &hci_sock_ops; + +- sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hci_sk_proto, kern); ++ sk = bt_sock_alloc(net, sock, &hci_sk_proto, protocol, GFP_ATOMIC, ++ kern); + if (!sk) + return -ENOMEM; + +- sock_init_data(sock, sk); +- +- sock_reset_flag(sk, SOCK_ZAPPED); +- +- sk->sk_protocol = protocol; +- + sock->state = SS_UNCONNECTED; +- sk->sk_state = BT_OPEN; + sk->sk_destruct = hci_sock_destruct; + + bt_sock_link(&hci_sk_list, sk); +--- a/net/bluetooth/l2cap_sock.c ++++ b/net/bluetooth/l2cap_sock.c +@@ -1901,21 +1901,13 @@ static struct sock *l2cap_sock_alloc(str + struct sock *sk; + struct l2cap_chan *chan; + +- sk = sk_alloc(net, PF_BLUETOOTH, prio, &l2cap_proto, kern); ++ sk = bt_sock_alloc(net, sock, &l2cap_proto, proto, prio, kern); + if (!sk) + return NULL; + +- sock_init_data(sock, sk); +- INIT_LIST_HEAD(&bt_sk(sk)->accept_q); +- + sk->sk_destruct = l2cap_sock_destruct; + sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; + +- sock_reset_flag(sk, SOCK_ZAPPED); +- +- sk->sk_protocol = proto; +- sk->sk_state = BT_OPEN; +- + chan = l2cap_chan_create(); + if (!chan) { + sk_free(sk); +--- a/net/bluetooth/rfcomm/sock.c ++++ b/net/bluetooth/rfcomm/sock.c +@@ -280,18 +280,16 @@ static struct proto rfcomm_proto = { + .obj_size = sizeof(struct rfcomm_pinfo) + }; + +-static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio, int kern) ++static struct sock *rfcomm_sock_alloc(struct net *net, struct socket *sock, ++ int proto, gfp_t prio, int kern) + { + struct rfcomm_dlc *d; + struct sock *sk; + +- sk = sk_alloc(net, PF_BLUETOOTH, prio, &rfcomm_proto, kern); ++ sk = bt_sock_alloc(net, sock, &rfcomm_proto, proto, prio, kern); + if (!sk) + return NULL; + +- sock_init_data(sock, sk); +- INIT_LIST_HEAD(&bt_sk(sk)->accept_q); +- + d = rfcomm_dlc_alloc(prio); + if (!d) { + sk_free(sk); +@@ -310,11 +308,6 @@ static struct sock *rfcomm_sock_alloc(st + sk->sk_sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; + sk->sk_rcvbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10; + +- sock_reset_flag(sk, SOCK_ZAPPED); +- +- sk->sk_protocol = proto; +- sk->sk_state = BT_OPEN; +- + bt_sock_link(&rfcomm_sk_list, sk); + + BT_DBG("sk %p", sk); +--- a/net/bluetooth/sco.c ++++ b/net/bluetooth/sco.c +@@ -491,21 +491,13 @@ static struct sock *sco_sock_alloc(struc + { + struct sock *sk; + +- sk = sk_alloc(net, PF_BLUETOOTH, prio, &sco_proto, kern); ++ sk = bt_sock_alloc(net, sock, &sco_proto, proto, prio, kern); + if (!sk) + return NULL; + +- sock_init_data(sock, sk); +- INIT_LIST_HEAD(&bt_sk(sk)->accept_q); +- + sk->sk_destruct = sco_sock_destruct; + sk->sk_sndtimeo = SCO_CONN_TIMEOUT; + +- sock_reset_flag(sk, SOCK_ZAPPED); +- +- sk->sk_protocol = proto; +- sk->sk_state = BT_OPEN; +- + sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT; + + bt_sock_link(&sco_sk_list, sk); diff --git a/queue-5.10/bluetooth-fix-uaf-in-l2cap_sock_cleanup_listen-vs-l2cap_conn_del.patch b/queue-5.10/bluetooth-fix-uaf-in-l2cap_sock_cleanup_listen-vs-l2cap_conn_del.patch new file mode 100644 index 0000000000..8c5c27f2a8 --- /dev/null +++ b/queue-5.10/bluetooth-fix-uaf-in-l2cap_sock_cleanup_listen-vs-l2cap_conn_del.patch @@ -0,0 +1,210 @@ +From stable+bounces-256730-greg=kroah.com@vger.kernel.org Sat May 30 00:49:16 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 15:18:12 -0400 +Subject: Bluetooth: fix UAF in l2cap_sock_cleanup_listen() vs l2cap_conn_del() +To: stable@vger.kernel.org +Cc: "Safa Karakuş" , "Siwei Zhang" , "Luiz Augusto von Dentz" , "Sasha Levin" +Message-ID: <20260529191812.1690614-1-sashal@kernel.org> + +From: Safa Karakuş + +[ Upstream commit ab1513597c6cf17cd1ad2a21e3b045421b48e022 ] + +bt_accept_dequeue() unlinks a not-yet-accepted child from the parent +accept queue and release_sock()s it before returning, so the returned +sk has no caller reference and is unlocked. + +l2cap_sock_cleanup_listen() walks these children on listening-socket +close. A concurrent HCI disconnect drives hci_rx_work -> +l2cap_conn_del() which runs l2cap_chan_del() + l2cap_sock_kill() and +frees the child sk and its l2cap_chan; cleanup_listen() then uses both: + + BUG: KASAN: slab-use-after-free in l2cap_sock_kill + l2cap_sock_kill / l2cap_sock_cleanup_listen / __x64_sys_close + Freed by: l2cap_conn_del -> l2cap_sock_close_cb -> l2cap_sock_kill + +This is distinct from the two fixes already in this area: commit +e83f5e24da741 ("Bluetooth: serialize accept_q access") serialises the +accept_q list/poll and takes temporary refs inside bt_accept_dequeue(), +and CVE-2025-39860 serialises the userspace close()/accept() race by +calling cleanup_listen() under lock_sock() in l2cap_sock_release(). +Neither covers l2cap_conn_del() running from hci_rx_work, so this UAF +still reproduces on current bluetooth/master. + +Take the reference at the source: bt_accept_dequeue() does sock_hold() +while sk is still locked, before release_sock(); callers sock_put(). +cleanup_listen() pins the chan with l2cap_chan_hold_unless_zero() under +a brief child sk lock (serialising vs l2cap_sock_teardown_cb()), drops +it before l2cap_chan_lock(), and skips a duplicate l2cap_sock_kill() on +SOCK_DEAD. conn->lock is not taken here: cleanup_listen() runs under +the parent sk lock and that would invert +conn->lock -> chan->lock -> sk_lock (lockdep). + +KASAN/SMP: an unprivileged listen/close vs HCI-disconnect race produced +12 use-after-free reports per run before this change; 0, and no lockdep +report, over 1600+ raced iterations after it on bluetooth/master. + +Fixes: 15f02b910562 ("Bluetooth: L2CAP: Add initial code for Enhanced Credit Based Mode") +Cc: stable@vger.kernel.org +Reported-by: Siwei Zhang +Reviewed-by: Siwei Zhang +Signed-off-by: Safa Karakuş +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/af_bluetooth.c | 10 ++++++++ + net/bluetooth/l2cap_sock.c | 51 +++++++++++++++++++++++++++++++++++++------ + net/bluetooth/rfcomm/sock.c | 9 ++++++- + net/bluetooth/sco.c | 9 ++++++- + 4 files changed, 70 insertions(+), 9 deletions(-) + +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -237,6 +237,16 @@ restart: + if (newsock) + sock_graft(sk, newsock); + ++ /* Hand the caller a reference taken while sk is ++ * still locked. bt_accept_unlink() just dropped ++ * the accept-queue reference; without this hold a ++ * concurrent teardown (e.g. l2cap_conn_del() -> ++ * l2cap_sock_kill()) could free sk between ++ * release_sock() and the caller using it. Every ++ * caller drops this with sock_put() when done. ++ */ ++ sock_hold(sk); ++ + release_sock(sk); + return sk; + } +--- a/net/bluetooth/l2cap_sock.c ++++ b/net/bluetooth/l2cap_sock.c +@@ -366,8 +366,13 @@ static int l2cap_sock_accept(struct sock + } + + nsk = bt_accept_dequeue(sk, newsock); +- if (nsk) ++ if (nsk) { ++ /* Drop the bridging ref from bt_accept_dequeue(); ++ * the grafted socket keeps nsk alive from here. ++ */ ++ sock_put(nsk); + break; ++ } + + if (!timeo) { + err = -EAGAIN; +@@ -1432,22 +1437,54 @@ static void l2cap_sock_cleanup_listen(st + BT_DBG("parent %p state %s", parent, + state_to_string(parent->sk_state)); + +- /* Close not yet accepted channels */ ++ /* Close not yet accepted channels. ++ * ++ * bt_accept_dequeue() now returns sk with an extra reference held ++ * (taken while sk was still locked) so a concurrent l2cap_conn_del() ++ * -> l2cap_sock_kill() cannot free sk under us. ++ * ++ * cleanup_listen() runs under the parent sk lock, so unlike ++ * l2cap_sock_shutdown() we must NOT take conn->lock here: that would ++ * establish sk_lock -> conn->lock and invert the established ++ * conn->lock -> chan->lock -> sk_lock order (lockdep deadlock). ++ * ++ * Instead, briefly take the child sk lock to fetch and pin its chan. ++ * l2cap_conn_del() reaches the chan free only via ++ * l2cap_chan_del() -> l2cap_sock_teardown_cb(), which itself takes ++ * the child sk lock; holding it across l2cap_chan_hold_unless_zero() ++ * therefore guarantees the chan cannot be freed while we read and ++ * pin it (hold_unless_zero() additionally skips a chan already past ++ * its last reference). We then drop the sk lock before taking ++ * chan->lock, so sk and chan locks are never held together. ++ */ + while ((sk = bt_accept_dequeue(parent, NULL))) { +- struct l2cap_chan *chan = l2cap_pi(sk)->chan; ++ struct l2cap_chan *chan; ++ ++ lock_sock_nested(sk, L2CAP_NESTING_NORMAL); ++ chan = l2cap_chan_hold_unless_zero(l2cap_pi(sk)->chan); ++ release_sock(sk); ++ if (!chan) { ++ /* l2cap_conn_del() already tearing this child down */ ++ sock_put(sk); ++ continue; ++ } + + BT_DBG("child chan %p state %s", chan, + state_to_string(chan->state)); + +- l2cap_chan_hold(chan); + l2cap_chan_lock(chan); +- + __clear_chan_timer(chan); + l2cap_chan_close(chan, ECONNRESET); +- l2cap_sock_kill(sk); +- ++ /* l2cap_conn_del() may already have killed this socket ++ * (it sets SOCK_DEAD); skip the duplicate to avoid a ++ * double sock_put()/l2cap_chan_put(). ++ */ ++ if (!sock_flag(sk, SOCK_DEAD)) ++ l2cap_sock_kill(sk); + l2cap_chan_unlock(chan); ++ + l2cap_chan_put(chan); ++ sock_put(sk); + } + } + +--- a/net/bluetooth/rfcomm/sock.c ++++ b/net/bluetooth/rfcomm/sock.c +@@ -190,6 +190,8 @@ static void rfcomm_sock_cleanup_listen(s + while ((sk = bt_accept_dequeue(parent, NULL))) { + rfcomm_sock_close(sk); + rfcomm_sock_kill(sk); ++ /* Drop the reference handed back by bt_accept_dequeue(). */ ++ sock_put(sk); + } + + parent->sk_state = BT_CLOSED; +@@ -508,8 +510,13 @@ static int rfcomm_sock_accept(struct soc + } + + nsk = bt_accept_dequeue(sk, newsock); +- if (nsk) ++ if (nsk) { ++ /* Drop the bridging ref from bt_accept_dequeue(); ++ * the grafted socket keeps nsk alive from here. ++ */ ++ sock_put(nsk); + break; ++ } + + if (!timeo) { + err = -EAGAIN; +--- a/net/bluetooth/sco.c ++++ b/net/bluetooth/sco.c +@@ -384,6 +384,8 @@ static void sco_sock_cleanup_listen(stru + while ((sk = bt_accept_dequeue(parent, NULL))) { + sco_sock_close(sk); + sco_sock_kill(sk); ++ /* Drop the reference handed back by bt_accept_dequeue(). */ ++ sock_put(sk); + } + + parent->sk_state = BT_CLOSED; +@@ -677,8 +679,13 @@ static int sco_sock_accept(struct socket + } + + ch = bt_accept_dequeue(sk, newsock); +- if (ch) ++ if (ch) { ++ /* Drop the bridging ref from bt_accept_dequeue(); ++ * the grafted socket keeps ch alive from here. ++ */ ++ sock_put(ch); + break; ++ } + + if (!timeo) { + err = -EAGAIN; diff --git a/queue-5.10/bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.patch b/queue-5.10/bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.patch new file mode 100644 index 0000000000..523e5e9d4a --- /dev/null +++ b/queue-5.10/bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.patch @@ -0,0 +1,94 @@ +From stable+bounces-244880-greg=kroah.com@vger.kernel.org Sat May 9 07:34:38 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 22:04:26 -0400 +Subject: Bluetooth: hci_event: fix potential UAF in SSP passkey handlers +To: stable@vger.kernel.org +Cc: Shuvam Pandey , Luiz Augusto von Dentz , Sasha Levin +Message-ID: <20260509020426.2866832-1-sashal@kernel.org> + +From: Shuvam Pandey + +[ Upstream commit 85fa3512048793076eef658f66489112dcc91993 ] + +hci_conn lookup and field access must be covered by hdev lock in +hci_user_passkey_notify_evt() and hci_keypress_notify_evt(), otherwise +the connection can be freed concurrently. + +Extend the hci_dev_lock critical section to cover all conn usage in both +handlers. + +Keep the existing keypress notification behavior unchanged by routing +the early exits through a common unlock path. + +Fixes: 92a25256f142 ("Bluetooth: mgmt: Implement support for passkey notification") +Cc: stable@vger.kernel.org +Signed-off-by: Shuvam Pandey +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/hci_event.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +--- a/net/bluetooth/hci_event.c ++++ b/net/bluetooth/hci_event.c +@@ -4842,9 +4842,11 @@ static void hci_user_passkey_notify_evt( + + BT_DBG("%s", hdev->name); + ++ hci_dev_lock(hdev); ++ + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (!conn) +- return; ++ goto unlock; + + conn->passkey_notify = __le32_to_cpu(ev->passkey); + conn->passkey_entered = 0; +@@ -4853,6 +4855,9 @@ static void hci_user_passkey_notify_evt( + mgmt_user_passkey_notify(hdev, &conn->dst, conn->type, + conn->dst_type, conn->passkey_notify, + conn->passkey_entered); ++ ++unlock: ++ hci_dev_unlock(hdev); + } + + static void hci_keypress_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) +@@ -4862,14 +4867,16 @@ static void hci_keypress_notify_evt(stru + + BT_DBG("%s", hdev->name); + ++ hci_dev_lock(hdev); ++ + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (!conn) +- return; ++ goto unlock; + + switch (ev->type) { + case HCI_KEYPRESS_STARTED: + conn->passkey_entered = 0; +- return; ++ goto unlock; + + case HCI_KEYPRESS_ENTERED: + conn->passkey_entered++; +@@ -4884,13 +4891,16 @@ static void hci_keypress_notify_evt(stru + break; + + case HCI_KEYPRESS_COMPLETED: +- return; ++ goto unlock; + } + + if (hci_dev_test_flag(hdev, HCI_MGMT)) + mgmt_user_passkey_notify(hdev, &conn->dst, conn->type, + conn->dst_type, conn->passkey_notify, + conn->passkey_entered); ++ ++unlock: ++ hci_dev_unlock(hdev); + } + + static void hci_simple_pair_complete_evt(struct hci_dev *hdev, diff --git a/queue-5.10/bluetooth-hci_qca-convert-timeout-from-jiffies-to-ms.patch b/queue-5.10/bluetooth-hci_qca-convert-timeout-from-jiffies-to-ms.patch new file mode 100644 index 0000000000..e610ff4971 --- /dev/null +++ b/queue-5.10/bluetooth-hci_qca-convert-timeout-from-jiffies-to-ms.patch @@ -0,0 +1,155 @@ +From stable+bounces-256802-greg=kroah.com@vger.kernel.org Sat May 30 04:51:08 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 19:20:59 -0400 +Subject: Bluetooth: hci_qca: Convert timeout from jiffies to ms +To: stable@vger.kernel.org +Cc: Shuai Zhang , Paul Menzel , Bartosz Golaszewski , Luiz Augusto von Dentz , Sasha Levin +Message-ID: <20260529232059.1870888-1-sashal@kernel.org> + +From: Shuai Zhang + +[ Upstream commit 375ba7484132662a4a8c7547d088fb6275c00282 ] + +Since the timer uses jiffies as its unit rather than ms, the timeout value +must be converted from ms to jiffies when configuring the timer. Otherwise, +the intended 8s timeout is incorrectly set to approximately 33s. + +To improve readability, embed msecs_to_jiffies() directly in the macro +definitions and drop the _MS suffix from macros that now yield jiffies +values: MEMDUMP_TIMEOUT, FW_DOWNLOAD_TIMEOUT, IBS_DISABLE_SSR_TIMEOUT, +CMD_TRANS_TIMEOUT, and IBS_BTSOC_TX_IDLE_TIMEOUT. + +IBS_WAKE_RETRANS_TIMEOUT_MS and IBS_HOST_TX_IDLE_TIMEOUT_MS are +intentionally left unchanged. Their values are stored in the struct fields +wake_retrans and tx_idle_delay, which hold ms values at runtime and can be +modified via debugfs. The msecs_to_jiffies() conversion happens at each +call site against the field value, so it cannot be embedded in the macro. + +Wake timer depends on commit c347ca17d62a + +Cc: stable@vger.kernel.org +Fixes: d841502c79e3 ("Bluetooth: hci_qca: Collect controller memory dump during SSR") +Reviewed-by: Paul Menzel +Acked-by: Bartosz Golaszewski +Signed-off-by: Shuai Zhang +Signed-off-by: Luiz Augusto von Dentz +[ adapted to `vmalloc`-based memdump path and older `qca_serdev_shutdown(struct device *dev)` signature ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/bluetooth/hci_qca.c | 33 ++++++++++++++++----------------- + 1 file changed, 16 insertions(+), 17 deletions(-) + +--- a/drivers/bluetooth/hci_qca.c ++++ b/drivers/bluetooth/hci_qca.c +@@ -46,13 +46,12 @@ + #define HCI_MAX_IBS_SIZE 10 + + #define IBS_WAKE_RETRANS_TIMEOUT_MS 100 +-#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 200 ++#define IBS_BTSOC_TX_IDLE_TIMEOUT msecs_to_jiffies(200) + #define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000 +-#define CMD_TRANS_TIMEOUT_MS 100 +-#define MEMDUMP_TIMEOUT_MS 8000 +-#define IBS_DISABLE_SSR_TIMEOUT_MS \ +- (MEMDUMP_TIMEOUT_MS + FW_DOWNLOAD_TIMEOUT_MS) +-#define FW_DOWNLOAD_TIMEOUT_MS 3000 ++#define CMD_TRANS_TIMEOUT msecs_to_jiffies(100) ++#define MEMDUMP_TIMEOUT msecs_to_jiffies(8000) ++#define FW_DOWNLOAD_TIMEOUT msecs_to_jiffies(3000) ++#define IBS_DISABLE_SSR_TIMEOUT (MEMDUMP_TIMEOUT + FW_DOWNLOAD_TIMEOUT) + + /* susclk rate */ + #define SUSCLK_RATE_32KHZ 32768 +@@ -1041,7 +1040,7 @@ static void qca_controller_memdump(struc + dump_size); + queue_delayed_work(qca->workqueue, + &qca->ctrl_memdump_timeout, +- msecs_to_jiffies(MEMDUMP_TIMEOUT_MS) ++ MEMDUMP_TIMEOUT + ); + + skb_pull(skb, sizeof(dump_size)); +@@ -1309,7 +1308,7 @@ static int qca_set_baudrate(struct hci_d + + if (hu->serdev) + serdev_device_wait_until_sent(hu->serdev, +- msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS)); ++ CMD_TRANS_TIMEOUT); + + /* Give the controller time to process the request */ + if (qca_is_wcn399x(qca_soc_type(hu))) +@@ -1330,8 +1329,8 @@ static inline void host_set_baudrate(str + + static int qca_send_power_pulse(struct hci_uart *hu, bool on) + { ++ int timeout = CMD_TRANS_TIMEOUT; + int ret; +- int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS); + u8 cmd = on ? QCA_WCN3990_POWERON_PULSE : QCA_WCN3990_POWEROFF_PULSE; + + /* These power pulses are single byte command which are sent +@@ -1490,7 +1489,7 @@ static void qca_wait_for_dump_collection + struct qca_data *qca = hu->priv; + + wait_on_bit_timeout(&qca->flags, QCA_MEMDUMP_COLLECTION, +- TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT_MS); ++ TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT); + + clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags); + } +@@ -2071,7 +2070,7 @@ static void qca_serdev_remove(struct ser + static void qca_serdev_shutdown(struct device *dev) + { + int ret; +- int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS); ++ int timeout = CMD_TRANS_TIMEOUT; + struct serdev_device *serdev = to_serdev_device(dev); + struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev); + struct hci_uart *hu = &qcadev->serdev_hu; +@@ -2129,7 +2128,7 @@ static int __maybe_unused qca_suspend(st + bool tx_pending = false; + int ret = 0; + u8 cmd; +- u32 wait_timeout = 0; ++ unsigned long wait_timeout = 0; + + set_bit(QCA_SUSPENDING, &qca->flags); + +@@ -2150,15 +2149,15 @@ static int __maybe_unused qca_suspend(st + if (test_bit(QCA_IBS_DISABLED, &qca->flags) || + test_bit(QCA_SSR_TRIGGERED, &qca->flags)) { + wait_timeout = test_bit(QCA_SSR_TRIGGERED, &qca->flags) ? +- IBS_DISABLE_SSR_TIMEOUT_MS : +- FW_DOWNLOAD_TIMEOUT_MS; ++ IBS_DISABLE_SSR_TIMEOUT : ++ FW_DOWNLOAD_TIMEOUT; + + /* QCA_IBS_DISABLED flag is set to true, During FW download + * and during memory dump collection. It is reset to false, + * After FW download complete. + */ + wait_on_bit_timeout(&qca->flags, QCA_IBS_DISABLED, +- TASK_UNINTERRUPTIBLE, msecs_to_jiffies(wait_timeout)); ++ TASK_UNINTERRUPTIBLE, wait_timeout); + + if (test_bit(QCA_IBS_DISABLED, &qca->flags)) { + bt_dev_err(hu->hdev, "SSR or FW download time out"); +@@ -2210,7 +2209,7 @@ static int __maybe_unused qca_suspend(st + + if (tx_pending) { + serdev_device_wait_until_sent(hu->serdev, +- msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS)); ++ CMD_TRANS_TIMEOUT); + serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu); + } + +@@ -2219,7 +2218,7 @@ static int __maybe_unused qca_suspend(st + */ + ret = wait_event_interruptible_timeout(qca->suspend_wait_q, + qca->rx_ibs_state == HCI_IBS_RX_ASLEEP, +- msecs_to_jiffies(IBS_BTSOC_TX_IDLE_TIMEOUT_MS)); ++ IBS_BTSOC_TX_IDLE_TIMEOUT); + if (ret == 0) { + ret = -ETIMEDOUT; + goto error; diff --git a/queue-5.10/bluetooth-init-sk_peer_-on-bt_sock_alloc.patch b/queue-5.10/bluetooth-init-sk_peer_-on-bt_sock_alloc.patch new file mode 100644 index 0000000000..cb1e1ee6c6 --- /dev/null +++ b/queue-5.10/bluetooth-init-sk_peer_-on-bt_sock_alloc.patch @@ -0,0 +1,139 @@ +From stable+bounces-256834-greg=kroah.com@vger.kernel.org Sat May 30 05:23:46 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 19:53:22 -0400 +Subject: Bluetooth: Init sk_peer_* on bt_sock_alloc +To: stable@vger.kernel.org +Cc: Luiz Augusto von Dentz , Sasha Levin +Message-ID: <20260529235323.1964735-2-sashal@kernel.org> + +From: Luiz Augusto von Dentz + +[ Upstream commit 464c702fb9374ff8f3f816f24fb7ac719dd20e1e ] + +This makes sure peer information is always available via sock when using +bt_sock_alloc. + +Signed-off-by: Luiz Augusto von Dentz +Stable-dep-of: e83f5e24da74 ("Bluetooth: serialize accept_q access") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bluetooth/af_bluetooth.c | 24 ++++++++++++++++++++++++ + net/bluetooth/hidp/sock.c | 10 +--------- + net/bluetooth/l2cap_sock.c | 19 ------------------- + 3 files changed, 25 insertions(+), 28 deletions(-) + +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -155,6 +155,14 @@ struct sock *bt_sock_alloc(struct net *n + sk->sk_protocol = proto; + sk->sk_state = BT_OPEN; + ++ /* Init peer information so it can be properly monitored */ ++ if (!kern) { ++ spin_lock(&sk->sk_peer_lock); ++ sk->sk_peer_pid = get_pid(task_tgid(current)); ++ sk->sk_peer_cred = get_current_cred(); ++ spin_unlock(&sk->sk_peer_lock); ++ } ++ + return sk; + } + EXPORT_SYMBOL(bt_sock_alloc); +@@ -177,6 +185,9 @@ EXPORT_SYMBOL(bt_sock_unlink); + + void bt_accept_enqueue(struct sock *parent, struct sock *sk, bool bh) + { ++ const struct cred *old_cred; ++ struct pid *old_pid; ++ + BT_DBG("parent %p, sk %p", parent, sk); + + sock_hold(sk); +@@ -189,6 +200,19 @@ void bt_accept_enqueue(struct sock *pare + list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); + bt_sk(sk)->parent = parent; + ++ /* Copy credentials from parent since for incoming connections the ++ * socket is allocated by the kernel. ++ */ ++ spin_lock(&sk->sk_peer_lock); ++ old_pid = sk->sk_peer_pid; ++ old_cred = sk->sk_peer_cred; ++ sk->sk_peer_pid = get_pid(parent->sk_peer_pid); ++ sk->sk_peer_cred = get_cred(parent->sk_peer_cred); ++ spin_unlock(&sk->sk_peer_lock); ++ ++ put_pid(old_pid); ++ put_cred(old_cred); ++ + if (bh) + bh_unlock_sock(sk); + else +--- a/net/bluetooth/hidp/sock.c ++++ b/net/bluetooth/hidp/sock.c +@@ -255,21 +255,13 @@ static int hidp_sock_create(struct net * + if (sock->type != SOCK_RAW) + return -ESOCKTNOSUPPORT; + +- sk = sk_alloc(net, PF_BLUETOOTH, GFP_ATOMIC, &hidp_proto, kern); ++ sk = bt_sock_alloc(net, sock, &hidp_proto, protocol, GFP_ATOMIC, kern); + if (!sk) + return -ENOMEM; + +- sock_init_data(sock, sk); +- + sock->ops = &hidp_sock_ops; +- + sock->state = SS_UNCONNECTED; + +- sock_reset_flag(sk, SOCK_ZAPPED); +- +- sk->sk_protocol = protocol; +- sk->sk_state = BT_OPEN; +- + bt_sock_link(&hidp_sk_list, sk); + + return 0; +--- a/net/bluetooth/l2cap_sock.c ++++ b/net/bluetooth/l2cap_sock.c +@@ -177,21 +177,6 @@ done: + return err; + } + +-static void l2cap_sock_init_pid(struct sock *sk) +-{ +- struct l2cap_chan *chan = l2cap_pi(sk)->chan; +- +- /* Only L2CAP_MODE_EXT_FLOWCTL ever need to access the PID in order to +- * group the channels being requested. +- */ +- if (chan->mode != L2CAP_MODE_EXT_FLOWCTL) +- return; +- +- spin_lock(&sk->sk_peer_lock); +- sk->sk_peer_pid = get_pid(task_tgid(current)); +- spin_unlock(&sk->sk_peer_lock); +-} +- + static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, + int alen, int flags) + { +@@ -267,8 +252,6 @@ static int l2cap_sock_connect(struct soc + chan->mode != L2CAP_MODE_EXT_FLOWCTL) + chan->mode = L2CAP_MODE_LE_FLOWCTL; + +- l2cap_sock_init_pid(sk); +- + err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), + &la.l2_bdaddr, la.l2_bdaddr_type); + if (err) +@@ -324,8 +307,6 @@ static int l2cap_sock_listen(struct sock + goto done; + } + +- l2cap_sock_init_pid(sk); +- + sk->sk_max_ack_backlog = backlog; + sk->sk_ack_backlog = 0; + diff --git a/queue-5.10/bluetooth-serialize-accept_q-access.patch b/queue-5.10/bluetooth-serialize-accept_q-access.patch new file mode 100644 index 0000000000..2d55d4cc50 --- /dev/null +++ b/queue-5.10/bluetooth-serialize-accept_q-access.patch @@ -0,0 +1,222 @@ +From stable+bounces-256835-greg=kroah.com@vger.kernel.org Sat May 30 05:23:33 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 19:53:23 -0400 +Subject: Bluetooth: serialize accept_q access +To: stable@vger.kernel.org +Cc: Jiexun Wang , Jann Horn , Yuan Tan , Yifan Wu , Juefei Pu , Xin Liu , Ren Wei , Luiz Augusto von Dentz , Sasha Levin +Message-ID: <20260529235323.1964735-3-sashal@kernel.org> + +From: Jiexun Wang + +[ Upstream commit e83f5e24da741fa9405aeeff00b08c5ee7c37b88 ] + +bt_sock_poll() walks the accept queue without synchronization, while +child teardown can unlink the same socket and drop its last reference. +The unsynchronized accept queue walk has existed since the initial +Bluetooth import. + +Protect accept_q with a dedicated lock for queue updates and polling. +Also rework bt_accept_dequeue() to take temporary child references under +the queue lock before dropping it and locking the child socket. + +Fixes: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 ("Linux-2.6.12-rc2") +Cc: stable@vger.kernel.org +Reported-by: Jann Horn +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Signed-off-by: Jiexun Wang +Signed-off-by: Ren Wei +Signed-off-by: Jiexun Wang +Reviewed-by: Jann Horn +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/net/bluetooth/bluetooth.h | 1 + net/bluetooth/af_bluetooth.c | 87 ++++++++++++++++++++++++++++---------- + 2 files changed, 66 insertions(+), 22 deletions(-) + +--- a/include/net/bluetooth/bluetooth.h ++++ b/include/net/bluetooth/bluetooth.h +@@ -291,6 +291,7 @@ void baswap(bdaddr_t *dst, const bdaddr_ + struct bt_sock { + struct sock sk; + struct list_head accept_q; ++ spinlock_t accept_q_lock; /* protects accept_q */ + struct sock *parent; + unsigned long flags; + void (*skb_msg_name)(struct sk_buff *, void *, int *); +--- a/net/bluetooth/af_bluetooth.c ++++ b/net/bluetooth/af_bluetooth.c +@@ -149,6 +149,7 @@ struct sock *bt_sock_alloc(struct net *n + + sock_init_data(sock, sk); + INIT_LIST_HEAD(&bt_sk(sk)->accept_q); ++ spin_lock_init(&bt_sk(sk)->accept_q_lock); + + sock_reset_flag(sk, SOCK_ZAPPED); + +@@ -187,6 +188,7 @@ void bt_accept_enqueue(struct sock *pare + { + const struct cred *old_cred; + struct pid *old_pid; ++ struct bt_sock *par = bt_sk(parent); + + BT_DBG("parent %p, sk %p", parent, sk); + +@@ -197,9 +199,13 @@ void bt_accept_enqueue(struct sock *pare + else + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + +- list_add_tail(&bt_sk(sk)->accept_q, &bt_sk(parent)->accept_q); + bt_sk(sk)->parent = parent; + ++ spin_lock_bh(&par->accept_q_lock); ++ list_add_tail(&bt_sk(sk)->accept_q, &par->accept_q); ++ sk_acceptq_added(parent); ++ spin_unlock_bh(&par->accept_q_lock); ++ + /* Copy credentials from parent since for incoming connections the + * socket is allocated by the kernel. + */ +@@ -217,8 +223,6 @@ void bt_accept_enqueue(struct sock *pare + bh_unlock_sock(sk); + else + release_sock(sk); +- +- sk_acceptq_added(parent); + } + EXPORT_SYMBOL(bt_accept_enqueue); + +@@ -227,45 +231,72 @@ EXPORT_SYMBOL(bt_accept_enqueue); + */ + void bt_accept_unlink(struct sock *sk) + { ++ struct sock *parent = bt_sk(sk)->parent; ++ + BT_DBG("sk %p state %d", sk, sk->sk_state); + ++ spin_lock_bh(&bt_sk(parent)->accept_q_lock); + list_del_init(&bt_sk(sk)->accept_q); +- sk_acceptq_removed(bt_sk(sk)->parent); ++ sk_acceptq_removed(parent); ++ spin_unlock_bh(&bt_sk(parent)->accept_q_lock); + bt_sk(sk)->parent = NULL; + sock_put(sk); + } + EXPORT_SYMBOL(bt_accept_unlink); + ++static struct sock *bt_accept_get(struct sock *parent, struct sock *sk) ++{ ++ struct bt_sock *bt = bt_sk(parent); ++ struct sock *next = NULL; ++ ++ /* accept_q is modified from child teardown paths too, so take a ++ * temporary reference before dropping the queue lock. ++ */ ++ spin_lock_bh(&bt->accept_q_lock); ++ ++ if (sk) { ++ if (bt_sk(sk)->parent != parent) ++ goto out; ++ ++ if (!list_is_last(&bt_sk(sk)->accept_q, &bt->accept_q)) { ++ next = &list_next_entry(bt_sk(sk), accept_q)->sk; ++ sock_hold(next); ++ } ++ } else if (!list_empty(&bt->accept_q)) { ++ next = &list_first_entry(&bt->accept_q, ++ struct bt_sock, accept_q)->sk; ++ sock_hold(next); ++ } ++ ++out: ++ spin_unlock_bh(&bt->accept_q_lock); ++ return next; ++} ++ + struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock) + { +- struct bt_sock *s, *n; +- struct sock *sk; ++ struct sock *sk, *next; + + BT_DBG("parent %p", parent); + + restart: +- list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) { +- sk = (struct sock *)s; +- ++ for (sk = bt_accept_get(parent, NULL); sk; sk = next) { + /* Prevent early freeing of sk due to unlink and sock_kill */ +- sock_hold(sk); + lock_sock(sk); + + /* Check sk has not already been unlinked via + * bt_accept_unlink() due to serialisation caused by sk locking + */ +- if (!bt_sk(sk)->parent) { ++ if (bt_sk(sk)->parent != parent) { + BT_DBG("sk %p, already unlinked", sk); + release_sock(sk); + sock_put(sk); + +- /* Restart the loop as sk is no longer in the list +- * and also avoid a potential infinite loop because +- * list_for_each_entry_safe() is not thread safe. +- */ + goto restart; + } + ++ next = bt_accept_get(parent, sk); ++ + /* sk is safely in the parent list so reduce reference count */ + sock_put(sk); + +@@ -293,6 +324,8 @@ restart: + sock_hold(sk); + + release_sock(sk); ++ if (next) ++ sock_put(next); + return sk; + } + +@@ -496,18 +529,28 @@ EXPORT_SYMBOL(bt_sock_stream_recvmsg); + + static inline __poll_t bt_accept_poll(struct sock *parent) + { +- struct bt_sock *s, *n; ++ struct bt_sock *bt = bt_sk(parent); ++ struct bt_sock *s; + struct sock *sk; ++ __poll_t mask = 0; ++ ++ spin_lock_bh(&bt->accept_q_lock); ++ list_for_each_entry(s, &bt->accept_q, accept_q) { ++ int state; + +- list_for_each_entry_safe(s, n, &bt_sk(parent)->accept_q, accept_q) { + sk = (struct sock *)s; +- if (sk->sk_state == BT_CONNECTED || +- (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags) && +- sk->sk_state == BT_CONNECT2)) +- return EPOLLIN | EPOLLRDNORM; ++ state = READ_ONCE(sk->sk_state); ++ ++ if (state == BT_CONNECTED || ++ (test_bit(BT_SK_DEFER_SETUP, &bt->flags) && ++ state == BT_CONNECT2)) { ++ mask = EPOLLIN | EPOLLRDNORM; ++ break; ++ } + } ++ spin_unlock_bh(&bt->accept_q_lock); + +- return 0; ++ return mask; + } + + __poll_t bt_sock_poll(struct file *file, struct socket *sock, diff --git a/queue-5.10/btrfs-fix-btrfs_ioctl_space_info-slot_count-toctou-which-can-lead-to-info-leak.patch b/queue-5.10/btrfs-fix-btrfs_ioctl_space_info-slot_count-toctou-which-can-lead-to-info-leak.patch new file mode 100644 index 0000000000..2774f3dc85 --- /dev/null +++ b/queue-5.10/btrfs-fix-btrfs_ioctl_space_info-slot_count-toctou-which-can-lead-to-info-leak.patch @@ -0,0 +1,59 @@ +From stable+bounces-247812-greg=kroah.com@vger.kernel.org Fri May 15 20:44:35 2026 +From: Sasha Levin +Date: Fri, 15 May 2026 11:07:04 -0400 +Subject: btrfs: fix btrfs_ioctl_space_info() slot_count TOCTOU which can lead to info-leak +To: stable@vger.kernel.org +Cc: Yochai Eisenrich , Yochai Eisenrich , David Sterba , Sasha Levin +Message-ID: <20260515150704.3261684-1-sashal@kernel.org> + +From: Yochai Eisenrich + +[ Upstream commit 973e57c726c1f8e77259d1c8e519519f1e9aea77 ] + +btrfs_ioctl_space_info() has a TOCTOU race between two passes over the +block group RAID type lists. The first pass counts entries to determine +the allocation size, then the second pass fills the buffer. The +groups_sem rwlock is released between passes, allowing concurrent block +group removal to reduce the entry count. + +When the second pass fills fewer entries than the first pass counted, +copy_to_user() copies the full alloc_size bytes including trailing +uninitialized kmalloc bytes to userspace. + +Fix by copying only total_spaces entries (the actually-filled count from +the second pass) instead of alloc_size bytes, and switch to kzalloc so +any future copy size mismatch cannot leak heap data. + +Fixes: 7fde62bffb57 ("Btrfs: buffer results in the space_info ioctl") +CC: stable@vger.kernel.org # 3.0 +Signed-off-by: Yochai Eisenrich +Reviewed-by: David Sterba +Signed-off-by: David Sterba +[ adapted upstream's `return -EFAULT;` to stable's `ret = -EFAULT;` fall-through to existing `out:` cleanup label ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/ioctl.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -3587,7 +3587,7 @@ static long btrfs_ioctl_space_info(struc + return -ENOMEM; + + space_args.total_spaces = 0; +- dest = kmalloc(alloc_size, GFP_KERNEL); ++ dest = kzalloc(alloc_size, GFP_KERNEL); + if (!dest) + return -ENOMEM; + dest_orig = dest; +@@ -3643,7 +3643,8 @@ static long btrfs_ioctl_space_info(struc + user_dest = (struct btrfs_ioctl_space_info __user *) + (arg + sizeof(struct btrfs_ioctl_space_args)); + +- if (copy_to_user(user_dest, dest_orig, alloc_size)) ++ if (copy_to_user(user_dest, dest_orig, ++ space_args.total_spaces * sizeof(*dest_orig))) + ret = -EFAULT; + + kfree(dest_orig); diff --git a/queue-5.10/btrfs-fix-missing-last_unlink_trans-update-when-removing-a-directory.patch b/queue-5.10/btrfs-fix-missing-last_unlink_trans-update-when-removing-a-directory.patch new file mode 100644 index 0000000000..87e8f2b39b --- /dev/null +++ b/queue-5.10/btrfs-fix-missing-last_unlink_trans-update-when-removing-a-directory.patch @@ -0,0 +1,224 @@ +From stable+bounces-249161-greg=kroah.com@vger.kernel.org Mon May 18 06:53:24 2026 +From: Sasha Levin +Date: Sun, 17 May 2026 21:23:18 -0400 +Subject: btrfs: fix missing last_unlink_trans update when removing a directory +To: stable@vger.kernel.org +Cc: Filipe Manana , Slava0135 , David Sterba , Sasha Levin +Message-ID: <20260518012318.481373-1-sashal@kernel.org> + +From: Filipe Manana + +[ Upstream commit 999757231c49376cd1a37308d2c8c4c9932571e1 ] + +When removing a directory we are not updating its last_unlink_trans field, +which can result in incorrect fsync behaviour in case some one fsyncs the +directory after it was removed because it's holding a file descriptor on +it. + +Example scenario: + + mkdir /mnt/dir1 + mkdir /mnt/dir1/dir2 + mkdir /mnt/dir3 + + sync -f /mnt + + # Do some change to the directory and fsync it. + chmod 700 /mnt/dir1 + xfs_io -c fsync /mnt/dir1 + + # Move dir2 out of dir1 so that dir1 becomes empty. + mv /mnt/dir1/dir2 /mnt/dir3/ + + open fd on /mnt/dir1 + call rmdir(2) on path "/mnt/dir1" + fsync fd + + + +When attempting to mount the filesystem, the log replay will fail with +an -EIO error and dmesg/syslog has the following: + + [445771.626482] BTRFS info (device dm-0): first mount of filesystem 0368bbea-6c5e-44b5-b409-09abe496e650 + [445771.626486] BTRFS info (device dm-0): using crc32c checksum algorithm + [445771.627912] BTRFS info (device dm-0): start tree-log replay + [445771.628335] page: refcount:2 mapcount:0 mapping:0000000061443ddc index:0x1d00 pfn:0x7072a5 + [445771.629453] memcg:ffff89f400351b00 + [445771.629892] aops:btree_aops [btrfs] ino:1 + [445771.630737] flags: 0x17fffc00000402a(uptodate|lru|private|writeback|node=0|zone=2|lastcpupid=0x1ffff) + [445771.632359] raw: 017fffc00000402a fffff47284d950c8 fffff472907b7c08 ffff89f458e412b8 + [445771.633713] raw: 0000000000001d00 ffff89f6c51d1a90 00000002ffffffff ffff89f400351b00 + [445771.635029] page dumped because: eb page dump + [445771.635825] BTRFS critical (device dm-0): corrupt leaf: root=5 block=30408704 slot=10 ino=258, invalid nlink: has 2 expect no more than 1 for dir + [445771.638088] BTRFS info (device dm-0): leaf 30408704 gen 10 total ptrs 17 free space 14878 owner 5 + [445771.638091] BTRFS info (device dm-0): refs 4 lock_owner 0 current 3581087 + [445771.638094] item 0 key (256 INODE_ITEM 0) itemoff 16123 itemsize 160 + [445771.638097] inode generation 3 transid 9 size 16 nbytes 16384 + [445771.638098] block group 0 mode 40755 links 1 uid 0 gid 0 + [445771.638100] rdev 0 sequence 2 flags 0x0 + [445771.638102] atime 1775744884.0 + [445771.660056] ctime 1775744885.645502983 + [445771.660058] mtime 1775744885.645502983 + [445771.660060] otime 1775744884.0 + [445771.660062] item 1 key (256 INODE_REF 256) itemoff 16111 itemsize 12 + [445771.660064] index 0 name_len 2 + [445771.660066] item 2 key (256 DIR_ITEM 1843588421) itemoff 16077 itemsize 34 + [445771.660068] location key (259 1 0) type 2 + [445771.660070] transid 9 data_len 0 name_len 4 + [445771.660075] item 3 key (256 DIR_ITEM 2363071922) itemoff 16043 itemsize 34 + [445771.660076] location key (257 1 0) type 2 + [445771.660077] transid 9 data_len 0 name_len 4 + [445771.660078] item 4 key (256 DIR_INDEX 2) itemoff 16009 itemsize 34 + [445771.660079] location key (257 1 0) type 2 + [445771.660080] transid 9 data_len 0 name_len 4 + [445771.660081] item 5 key (256 DIR_INDEX 3) itemoff 15975 itemsize 34 + [445771.660082] location key (259 1 0) type 2 + [445771.660083] transid 9 data_len 0 name_len 4 + [445771.660084] item 6 key (257 INODE_ITEM 0) itemoff 15815 itemsize 160 + [445771.660086] inode generation 9 transid 9 size 8 nbytes 0 + [445771.660087] block group 0 mode 40777 links 1 uid 0 gid 0 + [445771.660088] rdev 0 sequence 2 flags 0x0 + [445771.660089] atime 1775744885.641174097 + [445771.660090] ctime 1775744885.645502983 + [445771.660091] mtime 1775744885.645502983 + [445771.660105] otime 1775744885.641174097 + [445771.660106] item 7 key (257 INODE_REF 256) itemoff 15801 itemsize 14 + [445771.660107] index 2 name_len 4 + [445771.660108] item 8 key (257 DIR_ITEM 2676584006) itemoff 15767 itemsize 34 + [445771.660109] location key (258 1 0) type 2 + [445771.660110] transid 9 data_len 0 name_len 4 + [445771.660111] item 9 key (257 DIR_INDEX 2) itemoff 15733 itemsize 34 + [445771.660112] location key (258 1 0) type 2 + [445771.660113] transid 9 data_len 0 name_len 4 + [445771.660114] item 10 key (258 INODE_ITEM 0) itemoff 15573 itemsize 160 + [445771.660115] inode generation 9 transid 10 size 0 nbytes 0 + [445771.660116] block group 0 mode 40755 links 2 uid 0 gid 0 + [445771.660117] rdev 0 sequence 0 flags 0x0 + [445771.660118] atime 1775744885.645502983 + [445771.660119] ctime 1775744885.645502983 + [445771.660120] mtime 1775744885.645502983 + [445771.660121] otime 1775744885.645502983 + [445771.660122] item 11 key (258 INODE_REF 257) itemoff 15559 itemsize 14 + [445771.660123] index 2 name_len 4 + [445771.660124] item 12 key (258 INODE_REF 259) itemoff 15545 itemsize 14 + [445771.660125] index 2 name_len 4 + [445771.660126] item 13 key (259 INODE_ITEM 0) itemoff 15385 itemsize 160 + [445771.660127] inode generation 9 transid 10 size 8 nbytes 0 + [445771.660128] block group 0 mode 40755 links 1 uid 0 gid 0 + [445771.660129] rdev 0 sequence 1 flags 0x0 + [445771.660130] atime 1775744885.645502983 + [445771.660130] ctime 1775744885.645502983 + [445771.660131] mtime 1775744885.645502983 + [445771.660132] otime 1775744885.645502983 + [445771.660133] item 14 key (259 INODE_REF 256) itemoff 15371 itemsize 14 + [445771.660134] index 3 name_len 4 + [445771.660135] item 15 key (259 DIR_ITEM 2676584006) itemoff 15337 itemsize 34 + [445771.660136] location key (258 1 0) type 2 + [445771.660137] transid 10 data_len 0 name_len 4 + [445771.660138] item 16 key (259 DIR_INDEX 2) itemoff 15303 itemsize 34 + [445771.660139] location key (258 1 0) type 2 + [445771.660140] transid 10 data_len 0 name_len 4 + [445771.660144] BTRFS error (device dm-0): block=30408704 write time tree block corruption detected + [445771.661650] ------------[ cut here ]------------ + [445771.662358] WARNING: fs/btrfs/disk-io.c:326 at btree_csum_one_bio+0x217/0x230 [btrfs], CPU#8: mount/3581087 + [445771.663588] Modules linked in: btrfs f2fs xfs (...) + [445771.671229] CPU: 8 UID: 0 PID: 3581087 Comm: mount Tainted: G W 7.0.0-rc6-btrfs-next-230+ #2 PREEMPT(full) + [445771.672575] Tainted: [W]=WARN + [445771.672987] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org 04/01/2014 + [445771.674460] RIP: 0010:btree_csum_one_bio+0x217/0x230 [btrfs] + [445771.675222] Code: 89 44 24 (...) + [445771.677364] RSP: 0018:ffffd23882247660 EFLAGS: 00010246 + [445771.678029] RAX: 0000000000000000 RBX: ffff89f6c51d1a90 RCX: 0000000000000000 + [445771.678975] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffff89f406020000 + [445771.679983] RBP: ffff89f821204000 R08: 0000000000000000 R09: 00000000ffefffff + [445771.680905] R10: ffffd23882247448 R11: 0000000000000003 R12: ffffd23882247668 + [445771.681978] R13: ffff89f458e40fc0 R14: ffff89f737f4f500 R15: ffff89f737f4f500 + [445771.682912] FS: 00007f0447a98840(0000) GS:ffff89fb9771d000(0000) knlGS:0000000000000000 + [445771.684393] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 + [445771.685230] CR2: 00007f0447bf1330 CR3: 000000017cb02002 CR4: 0000000000370ef0 + [445771.686273] Call Trace: + [445771.686646] + [445771.686969] btrfs_submit_bbio+0x83f/0x860 [btrfs] + [445771.687750] ? write_one_eb+0x28f/0x340 [btrfs] + [445771.688428] btree_writepages+0x2e3/0x550 [btrfs] + [445771.689180] ? kmem_cache_alloc_noprof+0x12a/0x490 + [445771.689963] ? alloc_extent_state+0x19/0x120 [btrfs] + [445771.690801] ? kmem_cache_free+0x135/0x380 + [445771.691328] ? preempt_count_add+0x69/0xa0 + [445771.691831] ? set_extent_bit+0x252/0x8e0 [btrfs] + [445771.692468] ? xas_load+0x9/0xc0 + [445771.692873] ? xas_find+0x14d/0x1a0 + [445771.693304] do_writepages+0xc6/0x160 + [445771.693756] filemap_writeback+0xb8/0xe0 + [445771.694274] btrfs_write_marked_extents+0x61/0x170 [btrfs] + [445771.694999] btrfs_write_and_wait_transaction+0x4e/0xc0 [btrfs] + [445771.695818] btrfs_commit_transaction+0x5c8/0xd10 [btrfs] + [445771.696530] ? kmem_cache_free+0x135/0x380 + [445771.697120] ? release_extent_buffer+0x34/0x160 [btrfs] + [445771.697786] btrfs_recover_log_trees+0x7be/0x7e0 [btrfs] + [445771.698525] ? __pfx_replay_one_buffer+0x10/0x10 [btrfs] + [445771.699206] open_ctree+0x11e5/0x1810 [btrfs] + [445771.699776] btrfs_get_tree.cold+0xb/0x162 [btrfs] + [445771.700463] ? fscontext_read+0x165/0x180 + [445771.701146] ? rw_verify_area+0x50/0x180 + [445771.701866] vfs_get_tree+0x25/0xd0 + [445771.702491] vfs_cmd_create+0x59/0xe0 + [445771.703125] __do_sys_fsconfig+0x303/0x610 + [445771.703603] do_syscall_64+0xe9/0xf20 + [445771.703974] entry_SYSCALL_64_after_hwframe+0x76/0x7e + [445771.704700] RIP: 0033:0x7f0447cbd4aa + [445771.705108] Code: 73 01 c3 (...) + [445771.707263] RSP: 002b:00007ffc4e528318 EFLAGS: 00000246 ORIG_RAX: 00000000000001af + [445771.708107] RAX: ffffffffffffffda RBX: 00005561585d8c20 RCX: 00007f0447cbd4aa + [445771.708931] RDX: 0000000000000000 RSI: 0000000000000006 RDI: 0000000000000003 + [445771.709744] RBP: 00005561585d9120 R08: 0000000000000000 R09: 0000000000000000 + [445771.710674] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 + [445771.711477] R13: 00007f0447e4f580 R14: 00007f0447e5126c R15: 00007f0447e36a23 + [445771.712277] + [445771.712541] ---[ end trace 0000000000000000 ]--- + [445771.713382] BTRFS error (device dm-0): error while writing out transaction: -5 + [445771.714679] BTRFS warning (device dm-0): Skipping commit of aborted transaction. + [445771.715562] BTRFS error (device dm-0 state A): Transaction aborted (error -5) + [445771.716459] BTRFS: error (device dm-0 state A) in cleanup_transaction:2068: errno=-5 IO failure + [445771.717936] BTRFS error (device dm-0 state EA): failed to recover log trees with error: -5 + [445771.719681] BTRFS error (device dm-0 state EA): open_ctree failed: -5 + +The problem is that such a fsync should have result in a fallback to a +transaction commit, but that did not happen because through the +btrfs_rmdir() we never update the directory's last_unlink_trans field. +Any inode that had a link removed must have its last_unlink_trans updated +to the ID of transaction used for the operation, otherwise fsync and log +replay will not work correctly. + +btrfs_rmdir() calls btrfs_unlink_inode() and through that call chain we +never call btrfs_record_unlink_dir() in order to update last_unlink_trans. +However btrfs_unlink(), which is used for unlinking regular files, calls +btrfs_record_unlink_dir() and then calls btrfs_unlink_inode(). So fix +this by moving the call to btrfs_record_unlink_dir() from btrfs_unlink() +to btrfs_unlink_inode(). + +A test case for fstests will follow soon. + +Reported-by: Slava0135 +Link: https://lore.kernel.org/linux-btrfs/CAAJYhww5ov62Hm+n+tmhcL-e_4cBobg+OWogKjOJxVUXivC=MQ@mail.gmail.com/ +CC: stable@vger.kernel.org +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +[ wrapped dir and inode arguments with BTRFS_I() since 6.1 btrfs_rmdir() uses struct inode * instead of struct btrfs_inode * ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/btrfs/inode.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4178,6 +4178,8 @@ static int btrfs_rmdir(struct inode *dir + if (err) + goto out; + ++ btrfs_record_unlink_dir(trans, BTRFS_I(dir), BTRFS_I(inode), false); ++ + /* now the directory is empty */ + err = btrfs_unlink_inode(trans, root, BTRFS_I(dir), + BTRFS_I(d_inode(dentry)), dentry->d_name.name, diff --git a/queue-5.10/can-ucan-fix-devres-lifetime.patch b/queue-5.10/can-ucan-fix-devres-lifetime.patch new file mode 100644 index 0000000000..3d3c12c39d --- /dev/null +++ b/queue-5.10/can-ucan-fix-devres-lifetime.patch @@ -0,0 +1,44 @@ +From stable+bounces-244892-greg=kroah.com@vger.kernel.org Sat May 9 08:38:50 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 23:08:29 -0400 +Subject: can: ucan: fix devres lifetime +To: stable@vger.kernel.org +Cc: Johan Hovold , Jakob Unterwurzacher , Marc Kleine-Budde , Sasha Levin +Message-ID: <20260509030829.3038691-2-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit fed4626501c871890da287bec62a96e52da1af89 ] + +USB drivers bind to USB interfaces and any device managed resources +should have their lifetime tied to the interface rather than parent USB +device. This avoids issues like memory leaks when drivers are unbound +without their devices being physically disconnected (e.g. on probe +deferral or configuration changes). + +Fix the control message buffer lifetime so that it is released on driver +unbind. + +Fixes: 9f2d3eae88d2 ("can: ucan: add driver for Theobroma Systems UCAN devices") +Cc: stable@vger.kernel.org # 4.19 +Cc: Jakob Unterwurzacher +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260327104520.1310158-1-johan@kernel.org +Signed-off-by: Marc Kleine-Budde +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/can/usb/ucan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/can/usb/ucan.c ++++ b/drivers/net/can/usb/ucan.c +@@ -1394,7 +1394,7 @@ static int ucan_probe(struct usb_interfa + */ + + /* Prepare Memory for control transfers */ +- ctl_msg_buffer = devm_kzalloc(&udev->dev, ++ ctl_msg_buffer = devm_kzalloc(&intf->dev, + sizeof(union ucan_ctl_payload), + GFP_KERNEL); + if (!ctl_msg_buffer) { diff --git a/queue-5.10/can-ucan-fix-typos-in-comments.patch b/queue-5.10/can-ucan-fix-typos-in-comments.patch new file mode 100644 index 0000000000..d58a64f6ba --- /dev/null +++ b/queue-5.10/can-ucan-fix-typos-in-comments.patch @@ -0,0 +1,46 @@ +From stable+bounces-244891-greg=kroah.com@vger.kernel.org Sat May 9 08:38:37 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 23:08:28 -0400 +Subject: can: ucan: fix typos in comments +To: stable@vger.kernel.org +Cc: Julia Lawall , Marc Kleine-Budde , Sasha Levin +Message-ID: <20260509030829.3038691-1-sashal@kernel.org> + +From: Julia Lawall + +[ Upstream commit c34983c94166689358372d4af8d5def57752860c ] + +Various spelling mistakes in comments. +Detected with the help of Coccinelle. + +Link: https://lore.kernel.org/all/20220314115354.144023-28-Julia.Lawall@inria.fr +Signed-off-by: Julia Lawall +Acked-by: Marc Kleine-Budde +Signed-off-by: Marc Kleine-Budde +Stable-dep-of: fed4626501c8 ("can: ucan: fix devres lifetime") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/can/usb/ucan.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/net/can/usb/ucan.c ++++ b/drivers/net/can/usb/ucan.c +@@ -1393,7 +1393,7 @@ static int ucan_probe(struct usb_interfa + * Stage 3 for the final driver initialisation. + */ + +- /* Prepare Memory for control transferes */ ++ /* Prepare Memory for control transfers */ + ctl_msg_buffer = devm_kzalloc(&udev->dev, + sizeof(union ucan_ctl_payload), + GFP_KERNEL); +@@ -1527,7 +1527,7 @@ static int ucan_probe(struct usb_interfa + ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0, + sizeof(union ucan_ctl_payload)); + if (ret > 0) { +- /* copy string while ensuring zero terminiation */ ++ /* copy string while ensuring zero termination */ + strncpy(firmware_str, up->ctl_msg_buffer->raw, + sizeof(union ucan_ctl_payload)); + firmware_str[sizeof(union ucan_ctl_payload)] = '\0'; diff --git a/queue-5.10/ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch b/queue-5.10/ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch new file mode 100644 index 0000000000..210a3a9b71 --- /dev/null +++ b/queue-5.10/ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch @@ -0,0 +1,113 @@ +From stable+bounces-244967-greg=kroah.com@vger.kernel.org Sat May 9 21:03:36 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 11:33:30 -0400 +Subject: ceph: only d_add() negative dentries when they are unhashed +To: stable@vger.kernel.org +Cc: Max Kellermann , Viacheslav Dubeyko , Ilya Dryomov , Sasha Levin +Message-ID: <20260509153330.3523928-1-sashal@kernel.org> + +From: Max Kellermann + +[ Upstream commit 803447f93d75ab6e40c85e6d12b5630d281d70d6 ] + +Ceph can call d_add(dentry, NULL) on a negative dentry that is already +present in the primary dcache hash. + +In the current VFS that is not safe. d_add() goes through __d_add() +to __d_rehash(), which unconditionally reinserts dentry->d_hash into +the hlist_bl bucket. If the dentry is already hashed, reinserting the +same node can corrupt the bucket, including creating a self-loop. +Once that happens, __d_lookup() can spin forever in the hlist_bl walk, +typically looping only on the d_name.hash mismatch check and +eventually triggering RCU stall reports like this one: + + rcu: INFO: rcu_sched self-detected stall on CPU + rcu: 87-....: (2100 ticks this GP) idle=3a4c/1/0x4000000000000000 softirq=25003319/25003319 fqs=829 + rcu: (t=2101 jiffies g=79058445 q=698988 ncpus=192) + CPU: 87 UID: 2952868916 PID: 3933303 Comm: php-cgi8.3 Not tainted 6.18.17-i1-amd #950 NONE + Hardware name: Dell Inc. PowerEdge R7615/0G9DHV, BIOS 1.6.6 09/22/2023 + RIP: 0010:__d_lookup+0x46/0xb0 + Code: c1 e8 07 48 8d 04 c2 48 8b 00 49 89 fc 49 89 f5 48 89 c3 48 83 e3 fe 48 83 f8 01 77 0f eb 2d 0f 1f 44 00 00 48 8b 1b 48 85 db <74> 20 39 6b 18 75 f3 48 8d 7b 78 e8 ba 85 d0 00 4c 39 63 10 74 1f + RSP: 0018:ff745a70c8253898 EFLAGS: 00000282 + RAX: ff26e470054cb208 RBX: ff26e470054cb208 RCX: 000000006e958966 + RDX: ff26e48267340000 RSI: ff745a70c82539b0 RDI: ff26e458f74655c0 + RBP: 000000006e958966 R08: 0000000000000180 R09: 9cd08d909b919a89 + R10: ff26e458f74655c0 R11: 0000000000000000 R12: ff26e458f74655c0 + R13: ff745a70c82539b0 R14: d0d0d0d0d0d0d0d0 R15: 2f2f2f2f2f2f2f2f + FS: 00007f5770896980(0000) GS:ff26e482c5d88000(0000) knlGS:0000000000000000 + CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 + CR2: 00007f5764de50c0 CR3: 000000a72abb5001 CR4: 0000000000771ef0 + PKRU: 55555554 + Call Trace: + + lookup_fast+0x9f/0x100 + walk_component+0x1f/0x150 + link_path_walk+0x20e/0x3d0 + path_lookupat+0x68/0x180 + filename_lookup+0xdc/0x1e0 + vfs_statx+0x6c/0x140 + vfs_fstatat+0x67/0xa0 + __do_sys_newfstatat+0x24/0x60 + do_syscall_64+0x6a/0x230 + entry_SYSCALL_64_after_hwframe+0x76/0x7e + +This is reachable with reused cached negative dentries. A Ceph lookup +or atomic_open can be handed a negative dentry that is already hashed, +and fs/ceph/dir.c then hits one of two paths that incorrectly assume +"negative" also means "unhashed": + + - ceph_finish_lookup(): + MDS reply is -ENOENT with no trace + -> d_add(dentry, NULL) + + - ceph_lookup(): + local ENOENT fast path for a complete directory with shared caps + -> d_add(dentry, NULL) + +Both paths can therefore re-add an already-hashed negative dentry. + +Ceph already uses the correct pattern elsewhere: ceph_fill_trace() only +calls d_add(dn, NULL) for a negative null-dentry reply when d_unhashed(dn) +is true. + +Fix both fs/ceph/dir.c sites the same way: only call d_add() for a +negative dentry when it is actually unhashed. If the negative dentry +is already hashed, leave it in place and reuse it as-is. + +This preserves the existing behavior for unhashed dentries while +avoiding d_hash list corruption for reused hashed negatives. + +Cc: stable@vger.kernel.org +Fixes: 2817b000b02c ("ceph: directory operations") +Signed-off-by: Max Kellermann +Reviewed-by: Viacheslav Dubeyko +Signed-off-by: Ilya Dryomov +[ kept existing dout() debug call instead of upstream's doutc() form when adding the d_unhashed() guard around d_add() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ceph/dir.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/fs/ceph/dir.c ++++ b/fs/ceph/dir.c +@@ -719,7 +719,8 @@ struct dentry *ceph_finish_lookup(struct + d_drop(dentry); + err = -ENOENT; + } else { +- d_add(dentry, NULL); ++ if (d_unhashed(dentry)) ++ d_add(dentry, NULL); + } + } + } +@@ -775,7 +776,8 @@ static struct dentry *ceph_lookup(struct + __ceph_touch_fmode(ci, mdsc, CEPH_FILE_MODE_RD); + spin_unlock(&ci->i_ceph_lock); + dout(" dir %p complete, -ENOENT\n", dir); +- d_add(dentry, NULL); ++ if (d_unhashed(dentry)) ++ d_add(dentry, NULL); + di->lease_shared_gen = atomic_read(&ci->i_shared_gen); + return NULL; + } diff --git a/queue-5.10/crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch b/queue-5.10/crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch new file mode 100644 index 0000000000..ec3d42e477 --- /dev/null +++ b/queue-5.10/crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch @@ -0,0 +1,68 @@ +From stable+bounces-245037-greg=kroah.com@vger.kernel.org Sun May 10 21:13:50 2026 +From: Sasha Levin +Date: Sun, 10 May 2026 11:43:38 -0400 +Subject: crypto: caam - guard HMAC key hex dumps in hash_digest_key +To: stable@vger.kernel.org +Cc: Thorsten Blum , Herbert Xu , Sasha Levin +Message-ID: <20260510154339.144690-2-sashal@kernel.org> + +From: Thorsten Blum + +[ Upstream commit 177730a273b18e195263ed953853273e901b5064 ] + +Use print_hex_dump_devel() for dumping sensitive HMAC key bytes in +hash_digest_key() to avoid leaking secrets at runtime when +CONFIG_DYNAMIC_DEBUG is enabled. + +Fixes: 045e36780f11 ("crypto: caam - ahash hmac support") +Fixes: 3f16f6c9d632 ("crypto: caam/qi2 - add support for ahash algorithms") +Cc: stable@vger.kernel.org +Signed-off-by: Thorsten Blum +Signed-off-by: Herbert Xu +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/caam/caamalg_qi2.c | 4 ++-- + drivers/crypto/caam/caamhash.c | 4 ++-- + 2 files changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/crypto/caam/caamalg_qi2.c ++++ b/drivers/crypto/caam/caamalg_qi2.c +@@ -3261,7 +3261,7 @@ static int hash_digest_key(struct caam_h + dpaa2_fl_set_addr(out_fle, key_dma); + dpaa2_fl_set_len(out_fle, digestsize); + +- print_hex_dump_debug("key_in@" __stringify(__LINE__)": ", ++ print_hex_dump_devel("key_in@" __stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, key, *keylen, 1); + print_hex_dump_debug("shdesc@" __stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), +@@ -3281,7 +3281,7 @@ static int hash_digest_key(struct caam_h + /* in progress */ + wait_for_completion(&result.completion); + ret = result.err; +- print_hex_dump_debug("digested key@" __stringify(__LINE__)": ", ++ print_hex_dump_devel("digested key@" __stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, key, + digestsize, 1); + } +--- a/drivers/crypto/caam/caamhash.c ++++ b/drivers/crypto/caam/caamhash.c +@@ -390,7 +390,7 @@ static int hash_digest_key(struct caam_h + append_seq_store(desc, digestsize, LDST_CLASS_2_CCB | + LDST_SRCDST_BYTE_CONTEXT); + +- print_hex_dump_debug("key_in@"__stringify(__LINE__)": ", ++ print_hex_dump_devel("key_in@"__stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, key, *keylen, 1); + print_hex_dump_debug("jobdesc@"__stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc), +@@ -405,7 +405,7 @@ static int hash_digest_key(struct caam_h + wait_for_completion(&result.completion); + ret = result.err; + +- print_hex_dump_debug("digested key@"__stringify(__LINE__)": ", ++ print_hex_dump_devel("digested key@"__stringify(__LINE__)": ", + DUMP_PREFIX_ADDRESS, 16, 4, key, + digestsize, 1); + } diff --git a/queue-5.10/dm-btree-improve-btree-residency.patch b/queue-5.10/dm-btree-improve-btree-residency.patch new file mode 100644 index 0000000000..3031afc35f --- /dev/null +++ b/queue-5.10/dm-btree-improve-btree-residency.patch @@ -0,0 +1,622 @@ +From stable+bounces-249086-greg=kroah.com@vger.kernel.org Sun May 17 19:21:56 2026 +From: Sasha Levin +Date: Sun, 17 May 2026 09:51:48 -0400 +Subject: dm btree: improve btree residency +To: stable@vger.kernel.org +Cc: Joe Thornber , Colin Ian King , Mike Snitzer , Sasha Levin +Message-ID: <20260517135149.147613-1-sashal@kernel.org> + +From: Joe Thornber + +[ Upstream commit 4eafdb1515a708d97e4659bd488ddac19f274c4f ] + +This commit improves the residency of btrees built in the metadata for +dm-thin and dm-cache. + +When inserting a new entry into a full btree node the current code +splits the node into two. This can result in very many half full nodes, +particularly if the insertions are occurring in an ascending order (as +happens in dm-thin with large writes). + +With this commit, when we insert into a full node we first try and move +some entries to a neighbouring node that has space, failing that it +tries to split two neighbouring nodes into three. + +Results are given below. 'Residency' is how full nodes are on average +as a percentage. Average instruction counts for the operations +are given to show the extra processing has little overhead. + + +--------------------------+--------------------------+ + | Before | After | ++------------+-----------+-----------+--------------+-----------+--------------+ +| Test | Phase | Residency | Instructions | Residency | Instructions | ++------------+-----------+-----------+--------------+-----------+--------------+ +| Ascending | insert | 50 | 1876 | 96 | 1930 | +| | overwrite | 50 | 1789 | 96 | 1746 | +| | lookup | 50 | 778 | 96 | 778 | +| Descending | insert | 50 | 3024 | 96 | 3181 | +| | overwrite | 50 | 1789 | 96 | 1746 | +| | lookup | 50 | 778 | 96 | 778 | +| Random | insert | 68 | 3800 | 84 | 3736 | +| | overwrite | 68 | 4254 | 84 | 3911 | +| | lookup | 68 | 779 | 84 | 779 | +| Runs | insert | 63 | 2546 | 82 | 2815 | +| | overwrite | 63 | 2013 | 82 | 1986 | +| | lookup | 63 | 778 | 82 | 779 | ++------------+-----------+-----------+--------------+-----------+--------------+ + + Ascending - keys are inserted in ascending order. + Descending - keys are inserted in descending order. + Random - keys are inserted in random order. + Runs - keys are split into ascending runs of ~20 length. Then + the runs are shuffled. + +Signed-off-by: Joe Thornber +Signed-off-by: Colin Ian King # contains_key() fix +Signed-off-by: Mike Snitzer +Stable-dep-of: 09a65adc7d8b ("dm-thin: fix metadata refcount underflow") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/md/persistent-data/dm-btree.c | 451 ++++++++++++++++++-- + drivers/md/persistent-data/dm-transaction-manager.c | 9 + drivers/md/persistent-data/dm-transaction-manager.h | 10 + 3 files changed, 439 insertions(+), 31 deletions(-) + +--- a/drivers/md/persistent-data/dm-btree.c ++++ b/drivers/md/persistent-data/dm-btree.c +@@ -502,6 +502,122 @@ out: + + EXPORT_SYMBOL_GPL(dm_btree_lookup_next); + ++/*----------------------------------------------------------------*/ ++ ++/* ++ * Copies entries from one region of a btree node to another. The regions ++ * must not overlap. ++ */ ++static void copy_entries(struct btree_node *dest, unsigned dest_offset, ++ struct btree_node *src, unsigned src_offset, ++ unsigned count) ++{ ++ size_t value_size = le32_to_cpu(dest->header.value_size); ++ memcpy(dest->keys + dest_offset, src->keys + src_offset, count * sizeof(uint64_t)); ++ memcpy(value_ptr(dest, dest_offset), value_ptr(src, src_offset), count * value_size); ++} ++ ++/* ++ * Moves entries from one region fo a btree node to another. The regions ++ * may overlap. ++ */ ++static void move_entries(struct btree_node *dest, unsigned dest_offset, ++ struct btree_node *src, unsigned src_offset, ++ unsigned count) ++{ ++ size_t value_size = le32_to_cpu(dest->header.value_size); ++ memmove(dest->keys + dest_offset, src->keys + src_offset, count * sizeof(uint64_t)); ++ memmove(value_ptr(dest, dest_offset), value_ptr(src, src_offset), count * value_size); ++} ++ ++/* ++ * Erases the first 'count' entries of a btree node, shifting following ++ * entries down into their place. ++ */ ++static void shift_down(struct btree_node *n, unsigned count) ++{ ++ move_entries(n, 0, n, count, le32_to_cpu(n->header.nr_entries) - count); ++} ++ ++/* ++ * Moves entries in a btree node up 'count' places, making space for ++ * new entries at the start of the node. ++ */ ++static void shift_up(struct btree_node *n, unsigned count) ++{ ++ move_entries(n, count, n, 0, le32_to_cpu(n->header.nr_entries)); ++} ++ ++/* ++ * Redistributes entries between two btree nodes to make them ++ * have similar numbers of entries. ++ */ ++static void redistribute2(struct btree_node *left, struct btree_node *right) ++{ ++ unsigned nr_left = le32_to_cpu(left->header.nr_entries); ++ unsigned nr_right = le32_to_cpu(right->header.nr_entries); ++ unsigned total = nr_left + nr_right; ++ unsigned target_left = total / 2; ++ unsigned target_right = total - target_left; ++ ++ if (nr_left < target_left) { ++ unsigned delta = target_left - nr_left; ++ copy_entries(left, nr_left, right, 0, delta); ++ shift_down(right, delta); ++ } else if (nr_left > target_left) { ++ unsigned delta = nr_left - target_left; ++ if (nr_right) ++ shift_up(right, delta); ++ copy_entries(right, 0, left, target_left, delta); ++ } ++ ++ left->header.nr_entries = cpu_to_le32(target_left); ++ right->header.nr_entries = cpu_to_le32(target_right); ++} ++ ++/* ++ * Redistribute entries between three nodes. Assumes the central ++ * node is empty. ++ */ ++static void redistribute3(struct btree_node *left, struct btree_node *center, ++ struct btree_node *right) ++{ ++ unsigned nr_left = le32_to_cpu(left->header.nr_entries); ++ unsigned nr_center = le32_to_cpu(center->header.nr_entries); ++ unsigned nr_right = le32_to_cpu(right->header.nr_entries); ++ unsigned total, target_left, target_center, target_right; ++ ++ BUG_ON(nr_center); ++ ++ total = nr_left + nr_right; ++ target_left = total / 3; ++ target_center = (total - target_left) / 2; ++ target_right = (total - target_left - target_center); ++ ++ if (nr_left < target_left) { ++ unsigned left_short = target_left - nr_left; ++ copy_entries(left, nr_left, right, 0, left_short); ++ copy_entries(center, 0, right, left_short, target_center); ++ shift_down(right, nr_right - target_right); ++ ++ } else if (nr_left < (target_left + target_center)) { ++ unsigned left_to_center = nr_left - target_left; ++ copy_entries(center, 0, left, target_left, left_to_center); ++ copy_entries(center, left_to_center, right, 0, target_center - left_to_center); ++ shift_down(right, nr_right - target_right); ++ ++ } else { ++ unsigned right_short = target_right - nr_right; ++ shift_up(right, right_short); ++ copy_entries(right, 0, left, nr_left - right_short, right_short); ++ copy_entries(center, 0, left, target_left, nr_left - target_left); ++ } ++ ++ left->header.nr_entries = cpu_to_le32(target_left); ++ center->header.nr_entries = cpu_to_le32(target_center); ++ right->header.nr_entries = cpu_to_le32(target_right); ++} ++ + /* + * Splits a node by creating a sibling node and shifting half the nodes + * contents across. Assumes there is a parent node, and it has room for +@@ -532,12 +648,10 @@ EXPORT_SYMBOL_GPL(dm_btree_lookup_next); + * + * Where A* is a shadow of A. + */ +-static int btree_split_sibling(struct shadow_spine *s, unsigned parent_index, +- uint64_t key) ++static int split_one_into_two(struct shadow_spine *s, unsigned parent_index, ++ struct dm_btree_value_type *vt, uint64_t key) + { + int r; +- size_t size; +- unsigned nr_left, nr_right; + struct dm_block *left, *right, *parent; + struct btree_node *ln, *rn, *pn; + __le64 location; +@@ -551,36 +665,18 @@ static int btree_split_sibling(struct sh + ln = dm_block_data(left); + rn = dm_block_data(right); + +- nr_left = le32_to_cpu(ln->header.nr_entries) / 2; +- nr_right = le32_to_cpu(ln->header.nr_entries) - nr_left; +- +- ln->header.nr_entries = cpu_to_le32(nr_left); +- + rn->header.flags = ln->header.flags; +- rn->header.nr_entries = cpu_to_le32(nr_right); ++ rn->header.nr_entries = cpu_to_le32(0); + rn->header.max_entries = ln->header.max_entries; + rn->header.value_size = ln->header.value_size; +- memcpy(rn->keys, ln->keys + nr_left, nr_right * sizeof(rn->keys[0])); +- +- size = le32_to_cpu(ln->header.flags) & INTERNAL_NODE ? +- sizeof(uint64_t) : s->info->value_type.size; +- memcpy(value_ptr(rn, 0), value_ptr(ln, nr_left), +- size * nr_right); ++ redistribute2(ln, rn); + +- /* +- * Patch up the parent +- */ ++ /* patch up the parent */ + parent = shadow_parent(s); +- + pn = dm_block_data(parent); +- location = cpu_to_le64(dm_block_location(left)); +- __dm_bless_for_disk(&location); +- memcpy_disk(value_ptr(pn, parent_index), +- &location, sizeof(__le64)); + + location = cpu_to_le64(dm_block_location(right)); + __dm_bless_for_disk(&location); +- + r = insert_at(sizeof(__le64), pn, parent_index + 1, + le64_to_cpu(rn->keys[0]), &location); + if (r) { +@@ -588,6 +684,7 @@ static int btree_split_sibling(struct sh + return r; + } + ++ /* patch up the spine */ + if (key < le64_to_cpu(rn->keys[0])) { + unlock_block(s->info, right); + s->nodes[1] = left; +@@ -600,6 +697,121 @@ static int btree_split_sibling(struct sh + } + + /* ++ * We often need to modify a sibling node. This function shadows a particular ++ * child of the given parent node. Making sure to update the parent to point ++ * to the new shadow. ++ */ ++static int shadow_child(struct dm_btree_info *info, struct dm_btree_value_type *vt, ++ struct btree_node *parent, unsigned index, ++ struct dm_block **result) ++{ ++ int r, inc; ++ dm_block_t root; ++ struct btree_node *node; ++ ++ root = value64(parent, index); ++ ++ r = dm_tm_shadow_block(info->tm, root, &btree_node_validator, ++ result, &inc); ++ if (r) ++ return r; ++ ++ node = dm_block_data(*result); ++ ++ if (inc) ++ inc_children(info->tm, node, vt); ++ ++ *((__le64 *) value_ptr(parent, index)) = ++ cpu_to_le64(dm_block_location(*result)); ++ ++ return 0; ++} ++ ++/* ++ * Splits two nodes into three. This is more work, but results in fuller ++ * nodes, so saves metadata space. ++ */ ++static int split_two_into_three(struct shadow_spine *s, unsigned parent_index, ++ struct dm_btree_value_type *vt, uint64_t key) ++{ ++ int r; ++ unsigned middle_index; ++ struct dm_block *left, *middle, *right, *parent; ++ struct btree_node *ln, *rn, *mn, *pn; ++ __le64 location; ++ ++ parent = shadow_parent(s); ++ pn = dm_block_data(parent); ++ ++ if (parent_index == 0) { ++ middle_index = 1; ++ left = shadow_current(s); ++ r = shadow_child(s->info, vt, pn, parent_index + 1, &right); ++ if (r) ++ return r; ++ } else { ++ middle_index = parent_index; ++ right = shadow_current(s); ++ r = shadow_child(s->info, vt, pn, parent_index - 1, &left); ++ if (r) ++ return r; ++ } ++ ++ r = new_block(s->info, &middle); ++ if (r < 0) ++ return r; ++ ++ ln = dm_block_data(left); ++ mn = dm_block_data(middle); ++ rn = dm_block_data(right); ++ ++ mn->header.nr_entries = cpu_to_le32(0); ++ mn->header.flags = ln->header.flags; ++ mn->header.max_entries = ln->header.max_entries; ++ mn->header.value_size = ln->header.value_size; ++ ++ redistribute3(ln, mn, rn); ++ ++ /* patch up the parent */ ++ pn->keys[middle_index] = rn->keys[0]; ++ location = cpu_to_le64(dm_block_location(middle)); ++ __dm_bless_for_disk(&location); ++ r = insert_at(sizeof(__le64), pn, middle_index, ++ le64_to_cpu(mn->keys[0]), &location); ++ if (r) { ++ if (shadow_current(s) != left) ++ unlock_block(s->info, left); ++ ++ unlock_block(s->info, middle); ++ ++ if (shadow_current(s) != right) ++ unlock_block(s->info, right); ++ ++ return r; ++ } ++ ++ ++ /* patch up the spine */ ++ if (key < le64_to_cpu(mn->keys[0])) { ++ unlock_block(s->info, middle); ++ unlock_block(s->info, right); ++ s->nodes[1] = left; ++ } else if (key < le64_to_cpu(rn->keys[0])) { ++ unlock_block(s->info, left); ++ unlock_block(s->info, right); ++ s->nodes[1] = middle; ++ } else { ++ unlock_block(s->info, left); ++ unlock_block(s->info, middle); ++ s->nodes[1] = right; ++ } ++ ++ return 0; ++} ++ ++/*----------------------------------------------------------------*/ ++ ++/* + * Splits a node by creating two new children beneath the given node. + * + * Before: +@@ -692,6 +904,186 @@ static int btree_split_beneath(struct sh + return 0; + } + ++/*----------------------------------------------------------------*/ ++ ++/* ++ * Redistributes a node's entries with its left sibling. ++ */ ++static int rebalance_left(struct shadow_spine *s, struct dm_btree_value_type *vt, ++ unsigned parent_index, uint64_t key) ++{ ++ int r; ++ struct dm_block *sib; ++ struct btree_node *left, *right, *parent = dm_block_data(shadow_parent(s)); ++ ++ r = shadow_child(s->info, vt, parent, parent_index - 1, &sib); ++ if (r) ++ return r; ++ ++ left = dm_block_data(sib); ++ right = dm_block_data(shadow_current(s)); ++ redistribute2(left, right); ++ *key_ptr(parent, parent_index) = right->keys[0]; ++ ++ if (key < le64_to_cpu(right->keys[0])) { ++ unlock_block(s->info, s->nodes[1]); ++ s->nodes[1] = sib; ++ } else { ++ unlock_block(s->info, sib); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Redistributes a nodes entries with its right sibling. ++ */ ++static int rebalance_right(struct shadow_spine *s, struct dm_btree_value_type *vt, ++ unsigned parent_index, uint64_t key) ++{ ++ int r; ++ struct dm_block *sib; ++ struct btree_node *left, *right, *parent = dm_block_data(shadow_parent(s)); ++ ++ r = shadow_child(s->info, vt, parent, parent_index + 1, &sib); ++ if (r) ++ return r; ++ ++ left = dm_block_data(shadow_current(s)); ++ right = dm_block_data(sib); ++ redistribute2(left, right); ++ *key_ptr(parent, parent_index + 1) = right->keys[0]; ++ ++ if (key < le64_to_cpu(right->keys[0])) { ++ unlock_block(s->info, sib); ++ } else { ++ unlock_block(s->info, s->nodes[1]); ++ s->nodes[1] = sib; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Returns the number of spare entries in a node. ++ */ ++static int get_node_free_space(struct dm_btree_info *info, dm_block_t b, unsigned *space) ++{ ++ int r; ++ unsigned nr_entries; ++ struct dm_block *block; ++ struct btree_node *node; ++ ++ r = bn_read_lock(info, b, &block); ++ if (r) ++ return r; ++ ++ node = dm_block_data(block); ++ nr_entries = le32_to_cpu(node->header.nr_entries); ++ *space = le32_to_cpu(node->header.max_entries) - nr_entries; ++ ++ unlock_block(info, block); ++ return 0; ++} ++ ++/* ++ * Make space in a node, either by moving some entries to a sibling, ++ * or creating a new sibling node. SPACE_THRESHOLD defines the minimum ++ * number of free entries that must be in the sibling to make the move ++ * worth while. If the siblings are shared (eg, part of a snapshot), ++ * then they are not touched, since this break sharing and so consume ++ * more space than we save. ++ */ ++#define SPACE_THRESHOLD 8 ++static int rebalance_or_split(struct shadow_spine *s, struct dm_btree_value_type *vt, ++ unsigned parent_index, uint64_t key) ++{ ++ int r; ++ struct btree_node *parent = dm_block_data(shadow_parent(s)); ++ unsigned nr_parent = le32_to_cpu(parent->header.nr_entries); ++ unsigned free_space; ++ int left_shared = 0, right_shared = 0; ++ ++ /* Should we move entries to the left sibling? */ ++ if (parent_index > 0) { ++ dm_block_t left_b = value64(parent, parent_index - 1); ++ r = dm_tm_block_is_shared(s->info->tm, left_b, &left_shared); ++ if (r) ++ return r; ++ ++ if (!left_shared) { ++ r = get_node_free_space(s->info, left_b, &free_space); ++ if (r) ++ return r; ++ ++ if (free_space >= SPACE_THRESHOLD) ++ return rebalance_left(s, vt, parent_index, key); ++ } ++ } ++ ++ /* Should we move entries to the right sibling? */ ++ if (parent_index < (nr_parent - 1)) { ++ dm_block_t right_b = value64(parent, parent_index + 1); ++ r = dm_tm_block_is_shared(s->info->tm, right_b, &right_shared); ++ if (r) ++ return r; ++ ++ if (!right_shared) { ++ r = get_node_free_space(s->info, right_b, &free_space); ++ if (r) ++ return r; ++ ++ if (free_space >= SPACE_THRESHOLD) ++ return rebalance_right(s, vt, parent_index, key); ++ } ++ } ++ ++ /* ++ * We need to split the node, normally we split two nodes ++ * into three. But when inserting a sequence that is either ++ * monotonically increasing or decreasing it's better to split ++ * a single node into two. ++ */ ++ if (left_shared || right_shared || (nr_parent <= 2) || ++ (parent_index == 0) || (parent_index + 1 == nr_parent)) { ++ return split_one_into_two(s, parent_index, vt, key); ++ } else { ++ return split_two_into_three(s, parent_index, vt, key); ++ } ++} ++ ++/* ++ * Does the node contain a particular key? ++ */ ++static bool contains_key(struct btree_node *node, uint64_t key) ++{ ++ int i = lower_bound(node, key); ++ ++ if (i >= 0 && le64_to_cpu(node->keys[i]) == key) ++ return true; ++ ++ return false; ++} ++ ++/* ++ * In general we preemptively make sure there's a free entry in every ++ * node on the spine when doing an insert. But we can avoid that with ++ * leaf nodes if we know it's an overwrite. ++ */ ++static bool has_space_for_insert(struct btree_node *node, uint64_t key) ++{ ++ if (node->header.nr_entries == node->header.max_entries) { ++ if (le32_to_cpu(node->header.flags) & LEAF_NODE) { ++ /* we don't need space if it's an overwrite */ ++ return contains_key(node, key); ++ } ++ ++ return false; ++ } ++ ++ return true; ++} ++ + static int btree_insert_raw(struct shadow_spine *s, dm_block_t root, + struct dm_btree_value_type *vt, + uint64_t key, unsigned *index) +@@ -721,17 +1113,18 @@ static int btree_insert_raw(struct shado + + node = dm_block_data(shadow_current(s)); + +- if (node->header.nr_entries == node->header.max_entries) { ++ if (!has_space_for_insert(node, key)) { + if (top) + r = btree_split_beneath(s, key); + else +- r = btree_split_sibling(s, i, key); ++ r = rebalance_or_split(s, vt, i, key); + + if (r < 0) + return r; +- } + +- node = dm_block_data(shadow_current(s)); ++ /* making space can cause the current node to change */ ++ node = dm_block_data(shadow_current(s)); ++ } + + i = lower_bound(node, key); + +--- a/drivers/md/persistent-data/dm-transaction-manager.c ++++ b/drivers/md/persistent-data/dm-transaction-manager.c +@@ -379,6 +379,15 @@ int dm_tm_ref(struct dm_transaction_mana + return dm_sm_get_count(tm->sm, b, result); + } + ++int dm_tm_block_is_shared(struct dm_transaction_manager *tm, dm_block_t b, ++ int *result) ++{ ++ if (tm->is_clone) ++ return -EWOULDBLOCK; ++ ++ return dm_sm_count_is_more_than_one(tm->sm, b, result); ++} ++ + struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm) + { + return tm->bm; +--- a/drivers/md/persistent-data/dm-transaction-manager.h ++++ b/drivers/md/persistent-data/dm-transaction-manager.h +@@ -103,8 +103,14 @@ void dm_tm_inc(struct dm_transaction_man + + void dm_tm_dec(struct dm_transaction_manager *tm, dm_block_t b); + +-int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b, +- uint32_t *result); ++int dm_tm_ref(struct dm_transaction_manager *tm, dm_block_t b, uint32_t *result); ++ ++/* ++ * Finds out if a given block is shared (ie. has a reference count higher ++ * than one). ++ */ ++int dm_tm_block_is_shared(struct dm_transaction_manager *tm, dm_block_t b, ++ int *result); + + struct dm_block_manager *dm_tm_get_bm(struct dm_transaction_manager *tm); + diff --git a/queue-5.10/dm-thin-fix-metadata-refcount-underflow.patch b/queue-5.10/dm-thin-fix-metadata-refcount-underflow.patch new file mode 100644 index 0000000000..103e61f329 --- /dev/null +++ b/queue-5.10/dm-thin-fix-metadata-refcount-underflow.patch @@ -0,0 +1,59 @@ +From stable+bounces-249087-greg=kroah.com@vger.kernel.org Sun May 17 19:22:00 2026 +From: Sasha Levin +Date: Sun, 17 May 2026 09:51:49 -0400 +Subject: dm-thin: fix metadata refcount underflow +To: stable@vger.kernel.org +Cc: Mikulas Patocka , Sasha Levin +Message-ID: <20260517135149.147613-2-sashal@kernel.org> + +From: Mikulas Patocka + +[ Upstream commit 09a65adc7d8bbfce06392cb6d375468e2728ead5 ] + +There's a bug in dm-thin in the function rebalance_children. If the +internal btree node has one entry, the code tries to copy all btree +entries from the node's child to the node itself and then decrement the +child's reference count. + +If the child node is shared (it has reference count > 1), we won't free +it, so there would be two pointers to each of the grandchildren nodes. +But the reference counts of the grandchildren is not increased, thus the +reference count doesn't match the number of pointers that point to the +grandchildren. This results in "device mapper: space map common: unable +to decrement block" errors. + +Fix this bug by incrementing reference counts on the grandchildren if the +btree node is shared. + +Signed-off-by: Mikulas Patocka +Fixes: 3241b1d3e0aa ("dm: add persistent data library") +Cc: stable@vger.kernel.org +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/md/persistent-data/dm-btree-remove.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/drivers/md/persistent-data/dm-btree-remove.c ++++ b/drivers/md/persistent-data/dm-btree-remove.c +@@ -415,12 +415,20 @@ static int rebalance_children(struct sha + + if (le32_to_cpu(n->header.nr_entries) == 1) { + struct dm_block *child; ++ int is_shared; + dm_block_t b = value64(n, 0); + ++ r = dm_tm_block_is_shared(info->tm, b, &is_shared); ++ if (r) ++ return r; ++ + r = dm_tm_read_lock(info->tm, b, &btree_node_validator, &child); + if (r) + return r; + ++ if (is_shared) ++ inc_children(info->tm, dm_block_data(child), vt); ++ + memcpy(n, dm_block_data(child), + dm_bm_block_size(dm_tm_get_bm(info->tm))); + diff --git a/queue-5.10/drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch b/queue-5.10/drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch new file mode 100644 index 0000000000..b10dbd7785 --- /dev/null +++ b/queue-5.10/drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch @@ -0,0 +1,54 @@ +From sashal@kernel.org Tue Apr 28 14:32:50 2026 +From: Sasha Levin +Date: Tue, 28 Apr 2026 05:02:42 -0400 +Subject: drm/nouveau: fix u32 overflow in pushbuf reloc bounds check +To: stable@vger.kernel.org +Cc: Greg Kroah-Hartman , Lyude Paul , Danilo Krummrich , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , stable , Sasha Levin +Message-ID: <20260428090242.2131744-1-sashal@kernel.org> + +From: Greg Kroah-Hartman + +[ Upstream commit 2fc87d37be1b730a149b035f9375fdb8cc5333a5 ] + +nouveau_gem_pushbuf_reloc_apply() validates each relocation with + + if (r->reloc_bo_offset + 4 > nvbo->bo.base.size) + +but reloc_bo_offset is __u32 (uapi/drm/nouveau_drm.h) and the integer +literal 4 promotes to unsigned int, so the addition is performed in 32 +bits and wraps before the comparison against the size_t bo size. + +Cast to u64 so the addition happens in 64-bit arithmetic. + +Cc: Lyude Paul +Cc: Danilo Krummrich +Cc: Maarten Lankhorst +Cc: Maxime Ripard +Cc: Thomas Zimmermann +Cc: David Airlie +Cc: Simona Vetter +Reported-by: Anthropic +Cc: stable +Assisted-by: gkh_clanker_t1000 +Fixes: a1606a9596e5 ("drm/nouveau: new gem pushbuf interface, bump to 0.0.16") +Signed-off-by: Greg Kroah-Hartman +[ Add Fixes: tag. - Danilo ] +Signed-off-by: Danilo Krummrich +[ Kept 5.10's `nvbo->bo.mem.num_pages << PAGE_SHIFT` instead of upstream's `nvbo->bo.base.size` accessor. ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/gpu/drm/nouveau/nouveau_gem.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/gpu/drm/nouveau/nouveau_gem.c ++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c +@@ -618,7 +618,7 @@ nouveau_gem_pushbuf_reloc_apply(struct n + } + nvbo = (void *)(unsigned long)bo[r->reloc_bo_index].user_priv; + +- if (unlikely(r->reloc_bo_offset + 4 > ++ if (unlikely((u64)r->reloc_bo_offset + 4 > + nvbo->bo.mem.num_pages << PAGE_SHIFT)) { + NV_PRINTK(err, cli, "reloc outside of bo\n"); + ret = -EINVAL; diff --git a/queue-5.10/erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch b/queue-5.10/erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch new file mode 100644 index 0000000000..c102441040 --- /dev/null +++ b/queue-5.10/erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch @@ -0,0 +1,95 @@ +From stable+bounces-242495-greg=kroah.com@vger.kernel.org Sat May 2 00:42:00 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 15:11:50 -0400 +Subject: erofs: fix the out-of-bounds nameoff handling for trailing dirents +To: stable@vger.kernel.org +Cc: Gao Xiang , Yuhao Jiang , Junrui Luo , Chao Yu , Sasha Levin +Message-ID: <20260501191150.3973995-1-sashal@kernel.org> + +From: Gao Xiang + +[ Upstream commit d18a3b5d337fa412a38e776e6b4b857a58836575 ] + +Currently we already have boundary-checks for nameoffs, but the trailing +dirents are special since the namelens are calculated with strnlen() +with unchecked nameoffs. + +If a crafted EROFS has a trailing dirent with nameoff >= maxsize, +maxsize - nameoff can underflow, causing strnlen() to read past the +directory block. + +nameoff0 should also be verified to be a multiple of +`sizeof(struct erofs_dirent)` as well [1]. + +[1] https://sashiko.dev/#/patchset/20260416063511.3173774-1-hsiangkao%40linux.alibaba.com + +Fixes: 3aa8ec716e52 ("staging: erofs: add directory operations") +Fixes: 33bac912840f ("staging: erofs: keep corrupted fs from crashing kernel in erofs_readdir()") +Reported-by: Yuhao Jiang +Reported-by: Junrui Luo +Closes: https://lore.kernel.org/r/A0FD7E0F-7558-49B0-8BC8-EB1ECDB2479A@outlook.com +Cc: stable@vger.kernel.org +Signed-off-by: Gao Xiang +Reviewed-by: Chao Yu +[ replaced upstream `bsz` with `PAGE_SIZE` and `sizeof(*de)` with `sizeof(struct erofs_dirent)` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/erofs/dir.c | 30 ++++++++++++++++-------------- + 1 file changed, 16 insertions(+), 14 deletions(-) + +--- a/fs/erofs/dir.c ++++ b/fs/erofs/dir.c +@@ -38,20 +38,18 @@ static int erofs_fill_dentries(struct in + nameoff = le16_to_cpu(de->nameoff); + de_name = (char *)dentry_blk + nameoff; + +- /* the last dirent in the block? */ +- if (de + 1 >= end) +- de_namelen = strnlen(de_name, maxsize - nameoff); +- else ++ /* non-trailing dirent in the directory block? */ ++ if (de + 1 < end) + de_namelen = le16_to_cpu(de[1].nameoff) - nameoff; ++ else if (maxsize <= nameoff) ++ goto err_bogus; ++ else ++ de_namelen = strnlen(de_name, maxsize - nameoff); + +- /* a corrupted entry is found */ +- if (nameoff + de_namelen > maxsize || +- de_namelen > EROFS_NAME_LEN) { +- erofs_err(dir->i_sb, "bogus dirent @ nid %llu", +- EROFS_I(dir)->nid); +- DBG_BUGON(1); +- return -EFSCORRUPTED; +- } ++ /* a corrupted entry is found (including negative namelen) */ ++ if (!in_range32(de_namelen, 1, EROFS_NAME_LEN) || ++ nameoff + de_namelen > maxsize) ++ goto err_bogus; + + debug_one_dentry(d_type, de_name, de_namelen); + if (!dir_emit(ctx, de_name, de_namelen, +@@ -63,6 +61,10 @@ static int erofs_fill_dentries(struct in + } + *ofs = maxsize; + return 0; ++err_bogus: ++ erofs_err(dir->i_sb, "bogus dirent @ nid %llu", EROFS_I(dir)->nid); ++ DBG_BUGON(1); ++ return -EFSCORRUPTED; + } + + static int erofs_readdir(struct file *f, struct dir_context *ctx) +@@ -96,8 +98,8 @@ static int erofs_readdir(struct file *f, + + nameoff = le16_to_cpu(de->nameoff); + +- if (nameoff < sizeof(struct erofs_dirent) || +- nameoff >= PAGE_SIZE) { ++ if (!nameoff || nameoff >= PAGE_SIZE || ++ (nameoff % sizeof(struct erofs_dirent))) { + erofs_err(dir->i_sb, + "invalid de[0].nameoff %u @ nid %llu", + nameoff, EROFS_I(dir)->nid); diff --git a/queue-5.10/f2fs-fix-incorrect-file-address-mapping-when-inline-inode-is-unwritten.patch b/queue-5.10/f2fs-fix-incorrect-file-address-mapping-when-inline-inode-is-unwritten.patch new file mode 100644 index 0000000000..a4b07c7be0 --- /dev/null +++ b/queue-5.10/f2fs-fix-incorrect-file-address-mapping-when-inline-inode-is-unwritten.patch @@ -0,0 +1,68 @@ +From stable+bounces-249895-greg=kroah.com@vger.kernel.org Wed May 20 17:25:19 2026 +From: Sasha Levin +Date: Wed, 20 May 2026 07:46:53 -0400 +Subject: f2fs: fix incorrect file address mapping when inline inode is unwritten +To: stable@vger.kernel.org +Cc: Yongpeng Yang , stable@kernel.org, Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260520114653.3469848-1-sashal@kernel.org> + +From: Yongpeng Yang + +[ Upstream commit 68a0178981a0f493295afa29f8880246e561494c ] + +When `fileinfo->fi_flags` does not have the `FIEMAP_FLAG_SYNC` bit set +and inline data has not been persisted yet, the physical address of the +extent is calculated incorrectly for unwritten inline inodes. + +root@vm:/mnt/f2fs# dd if=/dev/zero of=data.3k bs=3k count=1 +root@vm:/mnt/f2fs# f2fs_io fiemap 0 100 data.3k +Fiemap: offset = 0 len = 100 + logical addr. physical addr. length flags +0 0000000000000000 00000ffffffff16c 0000000000000c00 00000301 + +This patch fixes the issue by checking if the inode's address is valid. +If the inline inode is unwritten, set the physical address to 0 and +mark the extent with `FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_DELALLOC` +flags. + +Cc: stable@kernel.org +Fixes: 67f8cf3cee6f ("f2fs: support fiemap for inline_data") +Signed-off-by: Yongpeng Yang +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +[ renamed `ifolio` to `ipage` in `inline_data_addr()` and `F2FS_INODE()` calls ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/inline.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +--- a/fs/f2fs/inline.c ++++ b/fs/f2fs/inline.c +@@ -761,7 +761,7 @@ int f2fs_read_inline_dir(struct file *fi + int f2fs_inline_data_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo, __u64 start, __u64 len) + { +- __u64 byteaddr, ilen; ++ __u64 byteaddr = 0, ilen; + __u32 flags = FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_NOT_ALIGNED | + FIEMAP_EXTENT_LAST; + struct node_info ni; +@@ -794,9 +794,14 @@ int f2fs_inline_data_fiemap(struct inode + if (err) + goto out; + +- byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits; +- byteaddr += (char *)inline_data_addr(inode, ipage) - +- (char *)F2FS_INODE(ipage); ++ if (__is_valid_data_blkaddr(ni.blk_addr)) { ++ byteaddr = (__u64)ni.blk_addr << inode->i_sb->s_blocksize_bits; ++ byteaddr += (char *)inline_data_addr(inode, ipage) - ++ (char *)F2FS_INODE(ipage); ++ } else { ++ f2fs_bug_on(F2FS_I_SB(inode), ni.blk_addr != NEW_ADDR); ++ flags |= FIEMAP_EXTENT_DELALLOC | FIEMAP_EXTENT_UNKNOWN; ++ } + err = fiemap_fill_next_extent(fieinfo, start, byteaddr, ilen, flags); + trace_f2fs_fiemap(inode, start, byteaddr, ilen, flags, err); + out: diff --git a/queue-5.10/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch b/queue-5.10/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch new file mode 100644 index 0000000000..c319d881c5 --- /dev/null +++ b/queue-5.10/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch @@ -0,0 +1,76 @@ +From stable+bounces-240974-greg=kroah.com@vger.kernel.org Fri Apr 24 19:39:35 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:07:49 -0400 +Subject: f2fs: fix UAF caused by decrementing sbi->nr_pages[] in f2fs_write_end_io() +To: stable@vger.kernel.org +Cc: Yongpeng Yang , stable@kernel.org, syzbot+6e4cb1cac5efc96ea0ca@syzkaller.appspotmail.com, Chao Yu , Jaegeuk Kim , Sasha Levin +Message-ID: <20260424140749.2151644-1-sashal@kernel.org> + +From: Yongpeng Yang + +[ Upstream commit 2d9c4a4ed4eef1f82c5b16b037aee8bad819fd53 ] + +The xfstests case "generic/107" and syzbot have both reported a NULL +pointer dereference. + +The concurrent scenario that triggers the panic is as follows: + +F2FS_WB_CP_DATA write callback umount + - f2fs_write_checkpoint + - f2fs_wait_on_all_pages(sbi, F2FS_WB_CP_DATA) +- blk_mq_end_request + - bio_endio + - f2fs_write_end_io + : dec_page_count(sbi, F2FS_WB_CP_DATA) + : wake_up(&sbi->cp_wait) + - kill_f2fs_super + - kill_block_super + - f2fs_put_super + : iput(sbi->node_inode) + : sbi->node_inode = NULL + : f2fs_in_warm_node_list + - is_node_folio // sbi->node_inode is NULL and panic + +The root cause is that f2fs_put_super() calls iput(sbi->node_inode) and +sets sbi->node_inode to NULL after sbi->nr_pages[F2FS_WB_CP_DATA] is +decremented to zero. As a result, f2fs_in_warm_node_list() may +dereference a NULL node_inode when checking whether a folio belongs to +the node inode, leading to a panic. + +This patch fixes the issue by calling f2fs_in_warm_node_list() before +decrementing sbi->nr_pages[F2FS_WB_CP_DATA], thus preventing the +use-after-free condition. + +Cc: stable@kernel.org +Fixes: 50fa53eccf9f ("f2fs: fix to avoid broken of dnode block list") +Reported-by: syzbot+6e4cb1cac5efc96ea0ca@syzkaller.appspotmail.com +Signed-off-by: Yongpeng Yang +Reviewed-by: Chao Yu +Signed-off-by: Jaegeuk Kim +[ folio => page ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/data.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/fs/f2fs/data.c ++++ b/fs/f2fs/data.c +@@ -377,6 +377,8 @@ static void f2fs_write_end_io(struct bio + + f2fs_bug_on(sbi, page->mapping == NODE_MAPPING(sbi) && + page->index != nid_of_node(page)); ++ if (f2fs_in_warm_node_list(sbi, page)) ++ f2fs_del_fsync_node_entry(sbi, page); + + dec_page_count(sbi, type); + +@@ -388,8 +390,6 @@ static void f2fs_write_end_io(struct bio + wq_has_sleeper(&sbi->cp_wait)) + wake_up(&sbi->cp_wait); + +- if (f2fs_in_warm_node_list(sbi, page)) +- f2fs_del_fsync_node_entry(sbi, page); + clear_cold_data(page); + end_page_writeback(page); + } diff --git a/queue-5.10/fbcon-avoid-oob-font-access-if-console-rotation-fails.patch b/queue-5.10/fbcon-avoid-oob-font-access-if-console-rotation-fails.patch new file mode 100644 index 0000000000..cceaf4ee69 --- /dev/null +++ b/queue-5.10/fbcon-avoid-oob-font-access-if-console-rotation-fails.patch @@ -0,0 +1,56 @@ +From stable+bounces-247713-greg=kroah.com@vger.kernel.org Fri May 15 17:42:14 2026 +From: Sasha Levin +Date: Fri, 15 May 2026 07:56:13 -0400 +Subject: fbcon: Avoid OOB font access if console rotation fails +To: stable@vger.kernel.org +Cc: Thomas Zimmermann , Helge Deller , Sasha Levin +Message-ID: <20260515115613.3041015-1-sashal@kernel.org> + +From: Thomas Zimmermann + +[ Upstream commit e4ef723d8975a2694cc90733a6b888a5e2841842 ] + +Clear the font buffer if the reallocation during console rotation fails +in fbcon_rotate_font(). The putcs implementations for the rotated buffer +will return early in this case. See [1] for an example. + +Currently, fbcon_rotate_font() keeps the old buffer, which is too small +for the rotated font. Printing to the rotated console with a high-enough +character code will overflow the font buffer. + +v2: +- fix typos in commit message + +Signed-off-by: Thomas Zimmermann +Fixes: 6cc50e1c5b57 ("[PATCH] fbcon: Console Rotation - Add support to rotate font bitmap") +Cc: stable@vger.kernel.org # v2.6.15+ +Link: https://elixir.bootlin.com/linux/v6.19/source/drivers/video/fbdev/core/fbcon_ccw.c#L144 # [1] +Signed-off-by: Helge Deller +[ renamed `par` to `ops` to match the 6.12 local pointer name ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/video/fbdev/core/fbcon_rotate.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/drivers/video/fbdev/core/fbcon_rotate.c ++++ b/drivers/video/fbdev/core/fbcon_rotate.c +@@ -46,6 +46,10 @@ static int fbcon_rotate_font(struct fb_i + info->fbops->fb_sync(info); + + if (ops->fd_size < d_cellsize * len) { ++ kfree(ops->fontbuffer); ++ ops->fontbuffer = NULL; ++ ops->fd_size = 0; ++ + dst = kmalloc_array(len, d_cellsize, GFP_KERNEL); + + if (dst == NULL) { +@@ -54,7 +58,6 @@ static int fbcon_rotate_font(struct fb_i + } + + ops->fd_size = d_cellsize * len; +- kfree(ops->fontbuffer); + ops->fontbuffer = dst; + } + diff --git a/queue-5.10/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch b/queue-5.10/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch new file mode 100644 index 0000000000..875b6d32f3 --- /dev/null +++ b/queue-5.10/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch @@ -0,0 +1,142 @@ +From stable+bounces-244876-greg=kroah.com@vger.kernel.org Sat May 9 06:53:09 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 21:22:58 -0400 +Subject: hfsplus: fix held lock freed on hfsplus_fill_super() +To: stable@vger.kernel.org +Cc: Zilin Guan , Viacheslav Dubeyko , Sasha Levin +Message-ID: <20260509012258.2847375-2-sashal@kernel.org> + +From: Zilin Guan + +[ Upstream commit 90c500e4fd83fa33c09bc7ee23b6d9cc487ac733 ] + +hfsplus_fill_super() calls hfs_find_init() to initialize a search +structure, which acquires tree->tree_lock. If the subsequent call to +hfsplus_cat_build_key() fails, the function jumps to the out_put_root +error label without releasing the lock. The later cleanup path then +frees the tree data structure with the lock still held, triggering a +held lock freed warning. + +Fix this by adding the missing hfs_find_exit(&fd) call before jumping +to the out_put_root error label. This ensures that tree->tree_lock is +properly released on the error path. + +The bug was originally detected on v6.13-rc1 using an experimental +static analysis tool we are developing, and we have verified that the +issue persists in the latest mainline kernel. The tool is specifically +designed to detect memory management issues. It is currently under active +development and not yet publicly available. + +We confirmed the bug by runtime testing under QEMU with x86_64 defconfig, +lockdep enabled, and CONFIG_HFSPLUS_FS=y. To trigger the error path, we +used GDB to dynamically shrink the max_unistr_len parameter to 1 before +hfsplus_asc2uni() is called. This forces hfsplus_asc2uni() to naturally +return -ENAMETOOLONG, which propagates to hfsplus_cat_build_key() and +exercises the faulty error path. The following warning was observed +during mount: + + ========================= + WARNING: held lock freed! + 7.0.0-rc3-00016-gb4f0dd314b39 #4 Not tainted + ------------------------- + mount/174 is freeing memory ffff888103f92000-ffff888103f92fff, with a lock still held there! + ffff888103f920b0 (&tree->tree_lock){+.+.}-{4:4}, at: hfsplus_find_init+0x154/0x1e0 + 2 locks held by mount/174: + #0: ffff888103f960e0 (&type->s_umount_key#42/1){+.+.}-{4:4}, at: alloc_super.constprop.0+0x167/0xa40 + #1: ffff888103f920b0 (&tree->tree_lock){+.+.}-{4:4}, at: hfsplus_find_init+0x154/0x1e0 + + stack backtrace: + CPU: 2 UID: 0 PID: 174 Comm: mount Not tainted 7.0.0-rc3-00016-gb4f0dd314b39 #4 PREEMPT(lazy) + Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014 + Call Trace: + + dump_stack_lvl+0x82/0xd0 + debug_check_no_locks_freed+0x13a/0x180 + kfree+0x16b/0x510 + ? hfsplus_fill_super+0xcb4/0x18a0 + hfsplus_fill_super+0xcb4/0x18a0 + ? __pfx_hfsplus_fill_super+0x10/0x10 + ? srso_return_thunk+0x5/0x5f + ? bdev_open+0x65f/0xc30 + ? srso_return_thunk+0x5/0x5f + ? pointer+0x4ce/0xbf0 + ? trace_contention_end+0x11c/0x150 + ? __pfx_pointer+0x10/0x10 + ? srso_return_thunk+0x5/0x5f + ? bdev_open+0x79b/0xc30 + ? srso_return_thunk+0x5/0x5f + ? srso_return_thunk+0x5/0x5f + ? vsnprintf+0x6da/0x1270 + ? srso_return_thunk+0x5/0x5f + ? __mutex_unlock_slowpath+0x157/0x740 + ? __pfx_vsnprintf+0x10/0x10 + ? srso_return_thunk+0x5/0x5f + ? srso_return_thunk+0x5/0x5f + ? mark_held_locks+0x49/0x80 + ? srso_return_thunk+0x5/0x5f + ? srso_return_thunk+0x5/0x5f + ? irqentry_exit+0x17b/0x5e0 + ? trace_irq_disable.constprop.0+0x116/0x150 + ? __pfx_hfsplus_fill_super+0x10/0x10 + ? __pfx_hfsplus_fill_super+0x10/0x10 + get_tree_bdev_flags+0x302/0x580 + ? __pfx_get_tree_bdev_flags+0x10/0x10 + ? vfs_parse_fs_qstr+0x129/0x1a0 + ? __pfx_vfs_parse_fs_qstr+0x3/0x10 + vfs_get_tree+0x89/0x320 + fc_mount+0x10/0x1d0 + path_mount+0x5c5/0x21c0 + ? __pfx_path_mount+0x10/0x10 + ? trace_irq_enable.constprop.0+0x116/0x150 + ? trace_irq_enable.constprop.0+0x116/0x150 + ? srso_return_thunk+0x5/0x5f + ? srso_return_thunk+0x5/0x5f + ? kmem_cache_free+0x307/0x540 + ? user_path_at+0x51/0x60 + ? __x64_sys_mount+0x212/0x280 + ? srso_return_thunk+0x5/0x5f + __x64_sys_mount+0x212/0x280 + ? __pfx___x64_sys_mount+0x10/0x10 + ? srso_return_thunk+0x5/0x5f + ? trace_irq_enable.constprop.0+0x116/0x150 + ? srso_return_thunk+0x5/0x5f + do_syscall_64+0x111/0x680 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + RIP: 0033:0x7ffacad55eae + Code: 48 8b 0d 85 1f 0f 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 a5 00 00 8 + RSP: 002b:00007fff1ab55718 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5 + RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ffacad55eae + RDX: 000055740c64e5b0 RSI: 000055740c64e630 RDI: 000055740c651ab0 + RBP: 000055740c64e380 R08: 0000000000000000 R09: 0000000000000001 + R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 + R13: 000055740c64e5b0 R14: 000055740c651ab0 R15: 000055740c64e380 + + +After applying this patch, the warning no longer appears. + +Fixes: 89ac9b4d3d1a ("hfsplus: fix longname handling") +CC: stable@vger.kernel.org +Signed-off-by: Zilin Guan +Reviewed-by: Viacheslav Dubeyko +Tested-by: Viacheslav Dubeyko +Signed-off-by: Viacheslav Dubeyko +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/hfsplus/super.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/fs/hfsplus/super.c ++++ b/fs/hfsplus/super.c +@@ -539,8 +539,10 @@ static int hfsplus_fill_super(struct sup + if (err) + goto out_put_root; + err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); +- if (unlikely(err < 0)) ++ if (unlikely(err < 0)) { ++ hfs_find_exit(&fd); + goto out_put_root; ++ } + if (!hfsplus_brec_read_cat(&fd, &entry)) { + hfs_find_exit(&fd); + if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) { diff --git a/queue-5.10/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch b/queue-5.10/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch new file mode 100644 index 0000000000..82b5e3a893 --- /dev/null +++ b/queue-5.10/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch @@ -0,0 +1,189 @@ +From stable+bounces-244875-greg=kroah.com@vger.kernel.org Sat May 9 06:53:05 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 21:22:57 -0400 +Subject: hfsplus: fix uninit-value by validating catalog record size +To: stable@vger.kernel.org +Cc: Deepanshu Kartikey , syzbot+d80abb5b890d39261e72@syzkaller.appspotmail.com, Viacheslav Dubeyko , Charalampos Mitrodimas , Sasha Levin +Message-ID: <20260509012258.2847375-1-sashal@kernel.org> + +From: Deepanshu Kartikey + +[ Upstream commit b6b592275aeff184aa82fcf6abccd833fb71b393 ] + +Syzbot reported a KMSAN uninit-value issue in hfsplus_strcasecmp(). The +root cause is that hfs_brec_read() doesn't validate that the on-disk +record size matches the expected size for the record type being read. + +When mounting a corrupted filesystem, hfs_brec_read() may read less data +than expected. For example, when reading a catalog thread record, the +debug output showed: + + HFSPLUS_BREC_READ: rec_len=520, fd->entrylength=26 + HFSPLUS_BREC_READ: WARNING - entrylength (26) < rec_len (520) - PARTIAL READ! + +hfs_brec_read() only validates that entrylength is not greater than the +buffer size, but doesn't check if it's less than expected. It successfully +reads 26 bytes into a 520-byte structure and returns success, leaving 494 +bytes uninitialized. + +This uninitialized data in tmp.thread.nodeName then gets copied by +hfsplus_cat_build_key_uni() and used by hfsplus_strcasecmp(), triggering +the KMSAN warning when the uninitialized bytes are used as array indices +in case_fold(). + +Fix by introducing hfsplus_brec_read_cat() wrapper that: +1. Calls hfs_brec_read() to read the data +2. Validates the record size based on the type field: + - Fixed size for folder and file records + - Variable size for thread records (depends on string length) +3. Returns -EIO if size doesn't match expected + +For thread records, check against HFSPLUS_MIN_THREAD_SZ before reading +nodeName.length to avoid reading uninitialized data at call sites that +don't zero-initialize the entry structure. + +Also initialize the tmp variable in hfsplus_find_cat() as defensive +programming to ensure no uninitialized data even if validation is +bypassed. + +Reported-by: syzbot+d80abb5b890d39261e72@syzkaller.appspotmail.com +Closes: https://syzkaller.appspot.com/bug?extid=d80abb5b890d39261e72 +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Tested-by: syzbot+d80abb5b890d39261e72@syzkaller.appspotmail.com +Reviewed-by: Viacheslav Dubeyko +Tested-by: Viacheslav Dubeyko +Suggested-by: Charalampos Mitrodimas +Link: https://lore.kernel.org/all/20260120051114.1281285-1-kartikey406@gmail.com/ [v1] +Link: https://lore.kernel.org/all/20260121063109.1830263-1-kartikey406@gmail.com/ [v2] +Link: https://lore.kernel.org/all/20260212014233.2422046-1-kartikey406@gmail.com/ [v3] +Link: https://lore.kernel.org/all/20260214002100.436125-1-kartikey406@gmail.com/T/ [v4] +Link: https://lore.kernel.org/all/20260221061626.15853-1-kartikey406@gmail.com/T/ [v5] +Signed-off-by: Deepanshu Kartikey +Signed-off-by: Viacheslav Dubeyko +Link: https://lore.kernel.org/r/20260307010302.41547-1-kartikey406@gmail.com +Signed-off-by: Viacheslav Dubeyko +Stable-dep-of: 90c500e4fd83 ("hfsplus: fix held lock freed on hfsplus_fill_super()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/hfsplus/bfind.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ + fs/hfsplus/catalog.c | 4 +-- + fs/hfsplus/dir.c | 2 - + fs/hfsplus/hfsplus_fs.h | 9 ++++++++ + fs/hfsplus/super.c | 2 - + 5 files changed, 64 insertions(+), 4 deletions(-) + +--- a/fs/hfsplus/bfind.c ++++ b/fs/hfsplus/bfind.c +@@ -287,3 +287,54 @@ out: + fd->bnode = bnode; + return res; + } ++ ++/** ++ * hfsplus_brec_read_cat - read and validate a catalog record ++ * @fd: find data structure ++ * @entry: pointer to catalog entry to read into ++ * ++ * Reads a catalog record and validates its size matches the expected ++ * size based on the record type. ++ * ++ * Returns 0 on success, or negative error code on failure. ++ */ ++int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry) ++{ ++ int res; ++ u32 expected_size; ++ ++ res = hfs_brec_read(fd, entry, sizeof(hfsplus_cat_entry)); ++ if (res) ++ return res; ++ ++ /* Validate catalog record size based on type */ ++ switch (be16_to_cpu(entry->type)) { ++ case HFSPLUS_FOLDER: ++ expected_size = sizeof(struct hfsplus_cat_folder); ++ break; ++ case HFSPLUS_FILE: ++ expected_size = sizeof(struct hfsplus_cat_file); ++ break; ++ case HFSPLUS_FOLDER_THREAD: ++ case HFSPLUS_FILE_THREAD: ++ /* Ensure we have at least the fixed fields before reading nodeName.length */ ++ if (fd->entrylength < HFSPLUS_MIN_THREAD_SZ) { ++ pr_err("thread record too short (got %u)\n", fd->entrylength); ++ return -EIO; ++ } ++ expected_size = hfsplus_cat_thread_size(&entry->thread); ++ break; ++ default: ++ pr_err("unknown catalog record type %d\n", ++ be16_to_cpu(entry->type)); ++ return -EIO; ++ } ++ ++ if (fd->entrylength != expected_size) { ++ pr_err("catalog record size mismatch (type %d, got %u, expected %u)\n", ++ be16_to_cpu(entry->type), fd->entrylength, expected_size); ++ return -EIO; ++ } ++ ++ return 0; ++} +--- a/fs/hfsplus/catalog.c ++++ b/fs/hfsplus/catalog.c +@@ -194,12 +194,12 @@ static int hfsplus_fill_cat_thread(struc + int hfsplus_find_cat(struct super_block *sb, u32 cnid, + struct hfs_find_data *fd) + { +- hfsplus_cat_entry tmp; ++ hfsplus_cat_entry tmp = {0}; + int err; + u16 type; + + hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid); +- err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry)); ++ err = hfsplus_brec_read_cat(fd, &tmp); + if (err) + return err; + +--- a/fs/hfsplus/dir.c ++++ b/fs/hfsplus/dir.c +@@ -49,7 +49,7 @@ static struct dentry *hfsplus_lookup(str + if (unlikely(err < 0)) + goto fail; + again: +- err = hfs_brec_read(&fd, &entry, sizeof(entry)); ++ err = hfsplus_brec_read_cat(&fd, &entry); + if (err) { + if (err == -ENOENT) { + hfs_find_exit(&fd); +--- a/fs/hfsplus/hfsplus_fs.h ++++ b/fs/hfsplus/hfsplus_fs.h +@@ -536,6 +536,15 @@ int hfsplus_submit_bio(struct super_bloc + void **data, int op, int op_flags); + int hfsplus_read_wrapper(struct super_block *sb); + ++static inline u32 hfsplus_cat_thread_size(const struct hfsplus_cat_thread *thread) ++{ ++ return offsetof(struct hfsplus_cat_thread, nodeName) + ++ offsetof(struct hfsplus_unistr, unicode) + ++ be16_to_cpu(thread->nodeName.length) * sizeof(hfsplus_unichr); ++} ++ ++int hfsplus_brec_read_cat(struct hfs_find_data *fd, hfsplus_cat_entry *entry); ++ + /* + * time helpers: convert between 1904-base and 1970-base timestamps + * +--- a/fs/hfsplus/super.c ++++ b/fs/hfsplus/super.c +@@ -541,7 +541,7 @@ static int hfsplus_fill_super(struct sup + err = hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); + if (unlikely(err < 0)) + goto out_put_root; +- if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { ++ if (!hfsplus_brec_read_cat(&fd, &entry)) { + hfs_find_exit(&fd); + if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) { + err = -EIO; diff --git a/queue-5.10/hv_netvsc-use-kmap_local_page-in-netvsc_copy_to_send_buf.patch b/queue-5.10/hv_netvsc-use-kmap_local_page-in-netvsc_copy_to_send_buf.patch new file mode 100644 index 0000000000..0328279fda --- /dev/null +++ b/queue-5.10/hv_netvsc-use-kmap_local_page-in-netvsc_copy_to_send_buf.patch @@ -0,0 +1,91 @@ +From stable+bounces-263454-greg=kroah.com@vger.kernel.org Tue Jun 16 02:19:50 2026 +From: Sasha Levin +Date: Mon, 15 Jun 2026 16:49:43 -0400 +Subject: hv_netvsc: use kmap_local_page in netvsc_copy_to_send_buf +To: stable@vger.kernel.org +Cc: Anton Leontev , Paolo Abeni , Sasha Levin +Message-ID: <20260615204943.2445304-1-sashal@kernel.org> + +From: Anton Leontev + +[ Upstream commit 004e9ecfe6c5384f9e0b2f6f6389d42ec22789af ] + +netvsc_copy_to_send_buf() copies page buffer entries into the VMBus +send buffer using phys_to_virt() on the entry PFN. Entries for the +RNDIS header and the skb linear data come from kmalloc'd memory and +are always in the kernel direct map, but entries for skb fragments +reference page cache or user pages, which on 32-bit x86 with +CONFIG_HIGHMEM=y can live above the LOWMEM boundary. For such a page +phys_to_virt() returns an address outside the direct map and the +subsequent memcpy() faults on the transmit softirq path, which is +fatal. + +Map the pages with kmap_local_page() instead, handling two properties +of the page buffer entries: + + - pb[i].pfn is a Hyper-V PFN at HV_HYP_PAGE_SIZE (4K) granularity, + not a native PFN. Reconstruct the physical address first and derive + the native page from it, so the mapping stays correct where + PAGE_SIZE > HV_HYP_PAGE_SIZE (e.g. arm64 with 64K pages). + + - Since commit 41a6328b2c55 ("hv_netvsc: Preserve contiguous PFN + grouping in the page buffer array"), an entry describes a full + physically contiguous fragment and pb[i].len can exceed PAGE_SIZE, + while kmap_local_page() maps a single page. Copy page by page, + splitting at native page boundaries. + +The copy path only handles packets smaller than the send section size +(6144 bytes by default); larger packets take the cp_partial path where +only the RNDIS header is copied. So entries here are bounded by the +section size and a copy is split at most once on 4K-page systems. On +!CONFIG_HIGHMEM configs kmap_local_page() folds to page_address() and +no mapping work is added. + +Fixes: c25aaf814a63 ("hyperv: Enable sendbuf mechanism on the send path") +Cc: stable@vger.kernel.org +Signed-off-by: Anton Leontev +Link: https://patch.msgid.link/20260604165938.32033-1-leontyevantony@gmail.com +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/hyperv/netvsc.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +--- a/drivers/net/hyperv/netvsc.c ++++ b/drivers/net/hyperv/netvsc.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -889,12 +890,22 @@ static void netvsc_copy_to_send_buf(stru + } + + for (i = 0; i < page_count; i++) { +- char *src = phys_to_virt(pb[i].pfn << HV_HYP_PAGE_SHIFT); +- u32 offset = pb[i].offset; ++ phys_addr_t paddr = (pb[i].pfn << HV_HYP_PAGE_SHIFT) + ++ pb[i].offset; + u32 len = pb[i].len; + +- memcpy(dest, (src + offset), len); +- dest += len; ++ while (len) { ++ struct page *page = pfn_to_page(PHYS_PFN(paddr)); ++ u32 off = offset_in_page(paddr); ++ u32 chunk = min_t(u32, len, PAGE_SIZE - off); ++ char *src = kmap_atomic(page); ++ ++ memcpy(dest, src + off, chunk); ++ kunmap_atomic(src); ++ dest += chunk; ++ paddr += chunk; ++ len -= chunk; ++ } + } + + if (padding) diff --git a/queue-5.10/ice-fix-vf-queue-configuration-with-low-mtu-values.patch b/queue-5.10/ice-fix-vf-queue-configuration-with-low-mtu-values.patch new file mode 100644 index 0000000000..99ad7250a2 --- /dev/null +++ b/queue-5.10/ice-fix-vf-queue-configuration-with-low-mtu-values.patch @@ -0,0 +1,62 @@ +From stable+bounces-256879-greg=kroah.com@vger.kernel.org Sat May 30 17:14:13 2026 +From: Sasha Levin +Date: Sat, 30 May 2026 07:44:06 -0400 +Subject: ice: fix VF queue configuration with low MTU values +To: stable@vger.kernel.org +Cc: Jose Ignacio Tornos Martinez , Jacob Keller , Michal Swiatkowski , Paul Menzel , Rafal Romanowski , Tony Nguyen , Jakub Kicinski , Sasha Levin +Message-ID: <20260530114406.1959724-1-sashal@kernel.org> + +From: Jose Ignacio Tornos Martinez + +[ Upstream commit 3ba4dd024d26372733d1c02e13e076c6016e3320 ] + +The ice driver's VF queue configuration validation rejects +databuffer_size values below 1024 bytes, which prevents VFs from +using MTU values below 871 bytes. + +The iavf driver calculates databuffer_size based on the MTU using: + databuffer_size = ALIGN(MTU + LIBETH_RX_LL_LEN, 128) + +where LIBETH_RX_LL_LEN = 26 (ETH_HLEN + 2*VLAN_HLEN + ETH_FCS_LEN). + +For MTU values below 871: + MTU 870: 870 + 26 = 896, aligned to 128 = 896 (< 1024, rejected) + MTU 871: 871 + 26 = 897, aligned to 128 = 1024 (>= 1024, accepted) + +The 1024-byte minimum seems unnecessarily restrictive, because the hardware +supports databuffer_size as low as 128 bytes (the alignment boundary), +which should allow MTU values down to the standard minimum of 68 bytes. + +I haven't found the reason why the limit was configured in the commit +9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message"), so +with no more information and since it is working, change the minimum +databuffer_size validation from 1024 to 128 bytes to allow standard low +MTU values while still preventing invalid configurations. + +Fixes: 9c7dd7566d18 ("ice: add validation in OP_CONFIG_VSI_QUEUES VF message") +cc: stable@vger.kernel.org +Signed-off-by: Jose Ignacio Tornos Martinez +Reviewed-by: Jacob Keller +Reviewed-by: Michal Swiatkowski +Reviewed-by: Paul Menzel +Tested-by: Rafal Romanowski +Signed-off-by: Tony Nguyen +Link: https://patch.msgid.link/20260515182419.1597859-3-anthony.l.nguyen@intel.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c ++++ b/drivers/net/ethernet/intel/ice/ice_virtchnl_pf.c +@@ -2990,7 +2990,7 @@ static int ice_vc_cfg_qs_msg(struct ice_ + + if (qpi->rxq.databuffer_size != 0 && + (qpi->rxq.databuffer_size > ((16 * 1024) - 128) || +- qpi->rxq.databuffer_size < 1024)) { ++ qpi->rxq.databuffer_size < 128)) { + v_ret = VIRTCHNL_STATUS_ERR_PARAM; + goto error_param; + } diff --git a/queue-5.10/ktest-fix-the-month-in-the-name-of-the-failure-directory.patch b/queue-5.10/ktest-fix-the-month-in-the-name-of-the-failure-directory.patch new file mode 100644 index 0000000000..c473a97739 --- /dev/null +++ b/queue-5.10/ktest-fix-the-month-in-the-name-of-the-failure-directory.patch @@ -0,0 +1,46 @@ +From stable+bounces-244964-greg=kroah.com@vger.kernel.org Sat May 9 20:52:32 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 11:22:22 -0400 +Subject: ktest: Fix the month in the name of the failure directory +To: stable@vger.kernel.org +Cc: Steven Rostedt , John 'Warthog9' Hawley , Sasha Levin +Message-ID: <20260509152222.3511536-2-sashal@kernel.org> + +From: Steven Rostedt + +[ Upstream commit 768059ede35f197575a38b10797b52402d9d4d2f ] + +The Perl localtime() function returns the month starting at 0 not 1. This +caused the date produced to create the directory for saving files of a +failed run to have the month off by one. + + machine-test-useconfig-fail-20260314073628 + +The above happened in April, not March. The correct name should have been: + + machine-test-useconfig-fail-20260414073628 + +This was somewhat confusing. + +Cc: stable@vger.kernel.org +Cc: John 'Warthog9' Hawley +Link: https://patch.msgid.link/20260420142426.33ad0293@fedora +Fixes: 7faafbd69639b ("ktest: Add open and close console and start stop monitor") +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/ktest/ktest.pl | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/tools/testing/ktest/ktest.pl ++++ b/tools/testing/ktest/ktest.pl +@@ -1739,7 +1739,7 @@ sub save_logs { + my ($result, $basedir) = @_; + my @t = localtime; + my $date = sprintf "%04d%02d%02d%02d%02d%02d", +- 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; ++ 1900+$t[5],$t[4]+1,$t[3],$t[2],$t[1],$t[0]; + + my $type = $build_type; + if ($type =~ /useconfig/) { diff --git a/queue-5.10/ktest-fixing-indentation-to-match-expected-pattern.patch b/queue-5.10/ktest-fixing-indentation-to-match-expected-pattern.patch new file mode 100644 index 0000000000..f9056c3b7d --- /dev/null +++ b/queue-5.10/ktest-fixing-indentation-to-match-expected-pattern.patch @@ -0,0 +1,372 @@ +From stable+bounces-244963-greg=kroah.com@vger.kernel.org Sat May 9 20:52:30 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 11:22:21 -0400 +Subject: ktest: Fixing indentation to match expected pattern +To: stable@vger.kernel.org +Cc: "John 'Warthog9' Hawley (VMware)" , "Steven Rostedt (VMware)" , Sasha Levin +Message-ID: <20260509152222.3511536-1-sashal@kernel.org> + +From: "John 'Warthog9' Hawley (VMware)" + +[ Upstream commit 12d4cddda2043466a5af8fc0c49e49f24f1d4c59 ] + +This is a followup to "ktest: Adding editor hints to improve +consistency" to actually adjust the existing indentation to match +the, now, expected pattern (first column 4 spaces, 2nd tab, 3rd +tab + 4 spaces, etc). This should, at least help, keep things +consistent going forward now. + +Signed-off-by: John 'Warthog9' Hawley (VMware) +Signed-off-by: Steven Rostedt (VMware) +Stable-dep-of: 768059ede35f ("ktest: Fix the month in the name of the failure directory") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/ktest/ktest.pl | 188 +++++++++++++++++++++---------------------- + 1 file changed, 93 insertions(+), 95 deletions(-) + +--- a/tools/testing/ktest/ktest.pl ++++ b/tools/testing/ktest/ktest.pl +@@ -763,7 +763,7 @@ sub process_variables { + # remove the space added in the beginning + $retval =~ s/ //; + +- return "$retval" ++ return "$retval"; + } + + sub set_value { +@@ -1099,7 +1099,7 @@ sub __read_config { + } + } + } +- ++ + if ( ! -r $file ) { + die "$name: $.: Can't read file $file\n$_"; + } +@@ -1190,13 +1190,13 @@ sub __read_config { + } + + sub get_test_case { +- print "What test case would you like to run?\n"; +- print " (build, install or boot)\n"; +- print " Other tests are available but require editing ktest.conf\n"; +- print " (see tools/testing/ktest/sample.conf)\n"; +- my $ans = ; +- chomp $ans; +- $default{"TEST_TYPE"} = $ans; ++ print "What test case would you like to run?\n"; ++ print " (build, install or boot)\n"; ++ print " Other tests are available but require editing ktest.conf\n"; ++ print " (see tools/testing/ktest/sample.conf)\n"; ++ my $ans = ; ++ chomp $ans; ++ $default{"TEST_TYPE"} = $ans; + } + + sub read_config { +@@ -1545,13 +1545,13 @@ sub dodie { + close O; + close L; + } +- send_email("KTEST: critical failure for test $i [$name]", +- "Your test started at $script_start_time has failed with:\n@_\n", $log_file); ++ send_email("KTEST: critical failure for test $i [$name]", ++ "Your test started at $script_start_time has failed with:\n@_\n", $log_file); + } + + if ($monitor_cnt) { +- # restore terminal settings +- system("stty $stty_orig"); ++ # restore terminal settings ++ system("stty $stty_orig"); + } + + if (defined($post_test)) { +@@ -1736,81 +1736,81 @@ sub wait_for_monitor { + } + + sub save_logs { +- my ($result, $basedir) = @_; +- my @t = localtime; +- my $date = sprintf "%04d%02d%02d%02d%02d%02d", +- 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; ++ my ($result, $basedir) = @_; ++ my @t = localtime; ++ my $date = sprintf "%04d%02d%02d%02d%02d%02d", ++ 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; + +- my $type = $build_type; +- if ($type =~ /useconfig/) { +- $type = "useconfig"; +- } ++ my $type = $build_type; ++ if ($type =~ /useconfig/) { ++ $type = "useconfig"; ++ } + +- my $dir = "$machine-$test_type-$type-$result-$date"; ++ my $dir = "$machine-$test_type-$type-$result-$date"; + +- $dir = "$basedir/$dir"; ++ $dir = "$basedir/$dir"; + +- if (!-d $dir) { +- mkpath($dir) or +- dodie "can't create $dir"; +- } ++ if (!-d $dir) { ++ mkpath($dir) or ++ dodie "can't create $dir"; ++ } + +- my %files = ( +- "config" => $output_config, +- "buildlog" => $buildlog, +- "dmesg" => $dmesg, +- "testlog" => $testlog, +- ); ++ my %files = ( ++ "config" => $output_config, ++ "buildlog" => $buildlog, ++ "dmesg" => $dmesg, ++ "testlog" => $testlog, ++ ); + +- while (my ($name, $source) = each(%files)) { +- if (-f "$source") { +- cp "$source", "$dir/$name" or +- dodie "failed to copy $source"; +- } ++ while (my ($name, $source) = each(%files)) { ++ if (-f "$source") { ++ cp "$source", "$dir/$name" or ++ dodie "failed to copy $source"; + } ++ } + +- doprint "*** Saved info to $dir ***\n"; ++ doprint "*** Saved info to $dir ***\n"; + } + + sub fail { + +- if ($die_on_failure) { +- dodie @_; +- } ++ if ($die_on_failure) { ++ dodie @_; ++ } + +- doprint "FAILED\n"; ++ doprint "FAILED\n"; + +- my $i = $iteration; ++ my $i = $iteration; + +- # no need to reboot for just building. +- if (!do_not_reboot) { +- doprint "REBOOTING\n"; +- reboot_to_good $sleep_time; +- } ++ # no need to reboot for just building. ++ if (!do_not_reboot) { ++ doprint "REBOOTING\n"; ++ reboot_to_good $sleep_time; ++ } + +- my $name = ""; ++ my $name = ""; + +- if (defined($test_name)) { +- $name = " ($test_name)"; +- } ++ if (defined($test_name)) { ++ $name = " ($test_name)"; ++ } + +- print_times; ++ print_times; + +- doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; +- doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; +- doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n"; +- doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; +- doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; +- +- if (defined($store_failures)) { +- save_logs "fail", $store_failures; +- } ++ doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; ++ doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; ++ doprint "KTEST RESULT: TEST $i$name Failed: ", @_, "\n"; ++ doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; ++ doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; + +- if (defined($post_test)) { +- run_command $post_test; +- } ++ if (defined($store_failures)) { ++ save_logs "fail", $store_failures; ++ } + +- return 1; ++ if (defined($post_test)) { ++ run_command $post_test; ++ } ++ ++ return 1; + } + + sub run_command { +@@ -2011,9 +2011,9 @@ sub get_grub_index { + $skip = '^\s*menuentry\s'; + $submenu = '^\s*submenu\s'; + } elsif ($reboot_type eq "grub2bls") { +- $command = $grub_bls_get; +- $target = '^title=.*' . $grub_menu_qt; +- $skip = '^title='; ++ $command = $grub_bls_get; ++ $target = '^title=.*' . $grub_menu_qt; ++ $skip = '^title='; + } else { + return; + } +@@ -2446,7 +2446,7 @@ sub check_buildlog { + while () { + if (/$check_build_re/) { + my $warning = process_warning_line $_; +- ++ + $warnings_list{$warning} = 1; + } + } +@@ -2708,7 +2708,7 @@ sub success { + doprint "*******************************************\n"; + + if (defined($store_successes)) { +- save_logs "success", $store_successes; ++ save_logs "success", $store_successes; + } + + if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) { +@@ -3293,13 +3293,13 @@ sub run_config_bisect { + + $ret = run_config_bisect_test $config_bisect_type; + if ($ret) { +- doprint "NEW GOOD CONFIG ($pass)\n"; ++ doprint "NEW GOOD CONFIG ($pass)\n"; + system("cp $output_config $tmpdir/good_config.tmp.$pass"); + $pass++; + # Return 3 for good config + return 3; + } else { +- doprint "NEW BAD CONFIG ($pass)\n"; ++ doprint "NEW BAD CONFIG ($pass)\n"; + system("cp $output_config $tmpdir/bad_config.tmp.$pass"); + $pass++; + # Return 4 for bad config +@@ -3418,7 +3418,7 @@ sub config_bisect { + } while ($ret == 3 || $ret == 4); + + if ($ret == 2) { +- config_bisect_end "$good_config.tmp", "$bad_config.tmp"; ++ config_bisect_end "$good_config.tmp", "$bad_config.tmp"; + } + + return $ret if ($ret < 0); +@@ -3598,7 +3598,6 @@ sub read_kconfig { + my $cont = 0; + my $line; + +- + if (! -f $kconfig) { + doprint "file $kconfig does not exist, skipping\n"; + return; +@@ -3707,7 +3706,7 @@ sub read_depends { + + if (! -f $kconfig && $arch =~ /\d$/) { + my $orig = $arch; +- # some subarchs have numbers, truncate them ++ # some subarchs have numbers, truncate them + $arch =~ s/\d*$//; + $kconfig = "$builddir/arch/$arch/Kconfig"; + if (! -f $kconfig) { +@@ -3903,7 +3902,7 @@ sub make_min_config { + foreach my $config (@config_keys) { + my $kconfig = chomp_config $config; + if (!defined $depcount{$kconfig}) { +- $depcount{$kconfig} = 0; ++ $depcount{$kconfig} = 0; + } + } + +@@ -4005,13 +4004,13 @@ sub make_min_config { + my $failed = 0; + build "oldconfig" or $failed = 1; + if (!$failed) { +- start_monitor_and_install or $failed = 1; ++ start_monitor_and_install or $failed = 1; + +- if ($type eq "test" && !$failed) { +- do_run_test or $failed = 1; +- } ++ if ($type eq "test" && !$failed) { ++ do_run_test or $failed = 1; ++ } + +- end_monitor; ++ end_monitor; + } + + $in_bisect = 0; +@@ -4330,8 +4329,8 @@ sub cancel_test { + } + if ($email_when_canceled) { + my $name = get_test_name; +- send_email("KTEST: Your [$name] test was cancelled", +- "Your test started at $script_start_time was cancelled: sig int"); ++ send_email("KTEST: Your [$name] test was cancelled", ++ "Your test started at $script_start_time was cancelled: sig int"); + } + run_post_ktest; + die "\nCaught Sig Int, test interrupted: $!\n" +@@ -4380,15 +4379,15 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; + + # The first test may override the PRE_KTEST option + if ($i == 1) { +- if (defined($pre_ktest)) { +- doprint "\n"; +- run_command $pre_ktest; +- } +- if ($email_when_started) { ++ if (defined($pre_ktest)) { ++ doprint "\n"; ++ run_command $pre_ktest; ++ } ++ if ($email_when_started) { + my $name = get_test_name; +- send_email("KTEST: Your [$name] test was started", +- "Your test was started on $script_start_time"); +- } ++ send_email("KTEST: Your [$name] test was started", ++ "Your test was started on $script_start_time"); ++ } + } + + # Any test can override the POST_KTEST option +@@ -4556,12 +4555,11 @@ if ($opt{"POWEROFF_ON_SUCCESS"}) { + run_command $switch_to_good; + } + +- + doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; + + if ($email_when_finished) { + send_email("KTEST: Your test has finished!", +- "$successes of $opt{NUM_TESTS} tests started at $script_start_time were successful!"); ++ "$successes of $opt{NUM_TESTS} tests started at $script_start_time were successful!"); + } + + if (defined($opt{"LOG_FILE"})) { diff --git a/queue-5.10/media-rc-igorplugusb-heed-coherency-rules.patch b/queue-5.10/media-rc-igorplugusb-heed-coherency-rules.patch new file mode 100644 index 0000000000..ee40410a42 --- /dev/null +++ b/queue-5.10/media-rc-igorplugusb-heed-coherency-rules.patch @@ -0,0 +1,93 @@ +From stable+bounces-242574-greg=kroah.com@vger.kernel.org Sat May 2 08:08:00 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 22:37:48 -0400 +Subject: media: rc: igorplugusb: heed coherency rules +To: stable@vger.kernel.org +Cc: Oliver Neukum , Sean Young , Hans Verkuil , Sasha Levin +Message-ID: <20260502023748.96270-1-sashal@kernel.org> + +From: Oliver Neukum + +[ Upstream commit eac69475b01fe1e861dfe3960b57fa95671c132e ] + +In a control request, the USB request structure +can be subject to DMA on some HCs. Hence it must obey +the rules for DMA coherency. Allocate it separately. + +Fixes: b1c97193c6437 ("[media] rc: port IgorPlug-USB to rc-core") +Cc: stable@vger.kernel.org +Signed-off-by: Oliver Neukum +Signed-off-by: Sean Young +Signed-off-by: Hans Verkuil +[ replaced kzalloc_obj() with kzalloc(sizeof(*ir->request), GFP_KERNEL) ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/rc/igorplugusb.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +--- a/drivers/media/rc/igorplugusb.c ++++ b/drivers/media/rc/igorplugusb.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -34,7 +35,7 @@ struct igorplugusb { + struct device *dev; + + struct urb *urb; +- struct usb_ctrlrequest request; ++ struct usb_ctrlrequest *request; + + struct timer_list timer; + +@@ -123,7 +124,7 @@ static void igorplugusb_cmd(struct igorp + { + int ret; + +- ir->request.bRequest = cmd; ++ ir->request->bRequest = cmd; + ir->urb->transfer_flags = 0; + ret = usb_submit_urb(ir->urb, GFP_ATOMIC); + if (ret) +@@ -165,13 +166,17 @@ static int igorplugusb_probe(struct usb_ + if (!ir) + return -ENOMEM; + ++ ir->request = kzalloc(sizeof(*ir->request), GFP_KERNEL); ++ if (!ir->request) ++ goto fail; ++ + ir->dev = &intf->dev; + + timer_setup(&ir->timer, igorplugusb_timer, 0); + +- ir->request.bRequest = GET_INFRACODE; +- ir->request.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; +- ir->request.wLength = cpu_to_le16(sizeof(ir->buf_in)); ++ ir->request->bRequest = GET_INFRACODE; ++ ir->request->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; ++ ir->request->wLength = cpu_to_le16(sizeof(ir->buf_in)); + + ir->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ir->urb) +@@ -223,6 +228,7 @@ fail: + rc_free_device(ir->rc); + usb_free_urb(ir->urb); + del_timer(&ir->timer); ++ kfree(ir->request); + + return ret; + } +@@ -236,6 +242,7 @@ static void igorplugusb_disconnect(struc + usb_set_intfdata(intf, NULL); + usb_kill_urb(ir->urb); + usb_free_urb(ir->urb); ++ kfree(ir->request); + } + + static const struct usb_device_id igorplugusb_table[] = { diff --git a/queue-5.10/media-rc-ttusbir-respect-dma-coherency-rules.patch b/queue-5.10/media-rc-ttusbir-respect-dma-coherency-rules.patch new file mode 100644 index 0000000000..6cea595462 --- /dev/null +++ b/queue-5.10/media-rc-ttusbir-respect-dma-coherency-rules.patch @@ -0,0 +1,83 @@ +From stable+bounces-242482-greg=kroah.com@vger.kernel.org Fri May 1 23:10:33 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 13:40:21 -0400 +Subject: media: rc: ttusbir: respect DMA coherency rules +To: stable@vger.kernel.org +Cc: Oliver Neukum , Sean Young , Hans Verkuil , Sasha Levin +Message-ID: <20260501174021.3858601-1-sashal@kernel.org> + +From: Oliver Neukum + +[ Upstream commit 50acaad3d202c064779db8dc3d010007347f59c7 ] + +Buffers must not share a cache line with other data structures. +Allocate separately. + +Fixes: 0938069fa0897 ("[media] rc: Add support for the TechnoTrend USB IR Receiver") +Cc: stable@vger.kernel.org +Signed-off-by: Oliver Neukum +Signed-off-by: Sean Young +Signed-off-by: Hans Verkuil +[ kept kzalloc(sizeof(*tt), GFP_KERNEL) instead of kzalloc_obj() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/rc/ttusbir.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +--- a/drivers/media/rc/ttusbir.c ++++ b/drivers/media/rc/ttusbir.c +@@ -32,7 +32,7 @@ struct ttusbir { + + struct led_classdev led; + struct urb *bulk_urb; +- uint8_t bulk_buffer[5]; ++ u8 *bulk_buffer; + int bulk_out_endp, iso_in_endp; + bool led_on, is_led_on; + atomic_t led_complete; +@@ -188,13 +188,16 @@ static int ttusbir_probe(struct usb_inte + struct rc_dev *rc; + int i, j, ret; + int altsetting = -1; ++ u8 *buffer; + + tt = kzalloc(sizeof(*tt), GFP_KERNEL); ++ buffer = kzalloc(5, GFP_KERNEL); + rc = rc_allocate_device(RC_DRIVER_IR_RAW); +- if (!tt || !rc) { ++ if (!tt || !rc || buffer) { + ret = -ENOMEM; + goto out; + } ++ tt->bulk_buffer = buffer; + + /* find the correct alt setting */ + for (i = 0; i < intf->num_altsetting && altsetting == -1; i++) { +@@ -283,8 +286,8 @@ static int ttusbir_probe(struct usb_inte + tt->bulk_buffer[3] = 0x01; + + usb_fill_bulk_urb(tt->bulk_urb, tt->udev, usb_sndbulkpipe(tt->udev, +- tt->bulk_out_endp), tt->bulk_buffer, sizeof(tt->bulk_buffer), +- ttusbir_bulk_complete, tt); ++ tt->bulk_out_endp), tt->bulk_buffer, 5, ++ ttusbir_bulk_complete, tt); + + tt->led.name = "ttusbir:green:power"; + tt->led.default_trigger = "rc-feedback"; +@@ -353,6 +356,7 @@ out: + kfree(tt); + } + rc_free_device(rc); ++ kfree(buffer); + + return ret; + } +@@ -375,6 +379,7 @@ static void ttusbir_disconnect(struct us + } + usb_kill_urb(tt->bulk_urb); + usb_free_urb(tt->bulk_urb); ++ kfree(tt->bulk_buffer); + usb_set_intfdata(intf, NULL); + kfree(tt); + } diff --git a/queue-5.10/mm-huge_memory-update-file-pmd-counter-before-folio_put.patch b/queue-5.10/mm-huge_memory-update-file-pmd-counter-before-folio_put.patch new file mode 100644 index 0000000000..6be0f1a60d --- /dev/null +++ b/queue-5.10/mm-huge_memory-update-file-pmd-counter-before-folio_put.patch @@ -0,0 +1,56 @@ +From stable+bounces-263527-greg=kroah.com@vger.kernel.org Tue Jun 16 08:47:59 2026 +From: Sasha Levin +Date: Mon, 15 Jun 2026 23:17:12 -0400 +Subject: mm/huge_memory: update file PMD counter before folio_put() +To: stable@vger.kernel.org +Cc: Yin Tirui , Lorenzo Stoakes , "David Hildenbrand (arm)" , Lance Yang , Dev Jain , Baolin Wang , Barry Song , Chen Jun , Kefeng Wang , "Liam R. Howlett" , Nico Pache , Ryan Roberts , Vlastimil Babka , Yang Shi , Zi Yan , Andrew Morton , Sasha Levin +Message-ID: <20260616031712.2780366-1-sashal@kernel.org> + +From: Yin Tirui + +[ Upstream commit 8d878059924f12c1bc24556a92ec56add74de3c8 ] + +__split_huge_pmd_locked() updates the file/shmem RSS counter after +dropping the PMD mapping's folio reference. If folio_put() drops the last +reference, mm_counter_file() can later read freed folio state via +folio_test_swapbacked(). + +Move the counter update before folio_put(). + +Link: https://lore.kernel.org/20260526101337.1984081-1-yintirui@huawei.com +Fixes: fadae2953072 ("thp: use mm_file_counter to determine update which rss counter") +Signed-off-by: Yin Tirui +Reviewed-by: Lorenzo Stoakes +Acked-by: David Hildenbrand (arm) +Reviewed-by: Lance Yang +Reviewed-by: Dev Jain +Cc: Baolin Wang +Cc: Barry Song +Cc: Chen Jun +Cc: Kefeng Wang +Cc: Liam R. Howlett +Cc: Nico Pache +Cc: Ryan Roberts +Cc: Vlastimil Babka +Cc: Yang Shi +Cc: Zi Yan +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + mm/huge_memory.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/mm/huge_memory.c ++++ b/mm/huge_memory.c +@@ -2066,7 +2066,9 @@ static void __split_huge_pmd_locked(stru + if (!PageReferenced(page) && pmd_young(old_pmd)) + SetPageReferenced(page); + page_remove_rmap(page, true); ++ add_mm_counter(mm, mm_counter_file(page), -HPAGE_PMD_NR); + put_page(page); ++ return; + } + add_mm_counter(mm, mm_counter_file(page), -HPAGE_PMD_NR); + return; diff --git a/queue-5.10/mm-hugetlb_cma-round-up-per_node-before-logging-it.patch b/queue-5.10/mm-hugetlb_cma-round-up-per_node-before-logging-it.patch new file mode 100644 index 0000000000..11d3649a8c --- /dev/null +++ b/queue-5.10/mm-hugetlb_cma-round-up-per_node-before-logging-it.patch @@ -0,0 +1,79 @@ +From stable+bounces-247268-greg=kroah.com@vger.kernel.org Thu May 14 23:28:55 2026 +From: Sasha Levin +Date: Thu, 14 May 2026 13:58:48 -0400 +Subject: mm/hugetlb_cma: round up per_node before logging it +To: stable@vger.kernel.org +Cc: Sang-Heon Jeon , Muchun Song , David Hildenbrand , Oscar Salvador , Andrew Morton , Sasha Levin +Message-ID: <20260514175848.530234-1-sashal@kernel.org> + +From: Sang-Heon Jeon + +[ Upstream commit 8f5ce56b76303c55b78a87af996e2e0f8535f979 ] + +When the user requests a total hugetlb CMA size without per-node +specification, hugetlb_cma_reserve() computes per_node from +hugetlb_cma_size and the number of nodes that have memory + + per_node = DIV_ROUND_UP(hugetlb_cma_size, + nodes_weight(hugetlb_bootmem_nodes)); + +The reservation loop later computes + + size = round_up(min(per_node, hugetlb_cma_size - reserved), + PAGE_SIZE << order); + +So the actually reserved per_node size is multiple of (PAGE_SIZE << +order), but the logged per_node is not rounded up, so it may be smaller +than the actual reserved size. + +For example, as the existing comment describes, if a 3 GB area is +requested on a machine with 4 NUMA nodes that have memory, 1 GB is +allocated on the first three nodes, but the printed log is + + hugetlb_cma: reserve 3072 MiB, up to 768 MiB per node + +Round per_node up to (PAGE_SIZE << order) before logging so that the +printed log always matches the actual reserved size. No functional change +to the actual reservation size, as the following case analysis shows + +1. remaining (hugetlb_cma_size - reserved) >= rounded per_node + - AS-IS: min() picks unrounded per_node; + round_up() returns rounded per_node + - TO-BE: min() picks rounded per_node; + round_up() returns rounded per_node (no-op) +2. remaining < unrounded per_node + - AS-IS: min() picks remaining; + round_up() returns round_up(remaining) + - TO-BE: min() picks remaining; + round_up() returns round_up(remaining) +3. unrounded per_node <= remaining < rounded per_node + - AS-IS: min() picks unrounded per_node; + round_up() returns rounded per_node + - TO-BE: min() picks remaining; + round_up() returns round_up(remaining) equals rounded per_node + +Link: https://lore.kernel.org/20260422143353.852257-1-ekffu200098@gmail.com +Fixes: cf11e85fc08c ("mm: hugetlb: optionally allocate gigantic hugepages using cma") # 5.7 +Signed-off-by: Sang-Heon Jeon +Reviewed-by: Muchun Song +Cc: David Hildenbrand +Cc: Oscar Salvador +Cc: +Signed-off-by: Andrew Morton +[ applied the one-line `round_up` to `mm/hugetlb.c` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + mm/hugetlb.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -5842,6 +5842,7 @@ void __init hugetlb_cma_reserve(int orde + * let's allocate 1 GB on first three nodes and ignore the last one. + */ + per_node = DIV_ROUND_UP(hugetlb_cma_size, nr_online_nodes); ++ per_node = round_up(per_node, PAGE_SIZE << order); + pr_info("hugetlb_cma: reserve %lu MiB, up to %lu MiB per node\n", + hugetlb_cma_size / SZ_1M, per_node / SZ_1M); + diff --git a/queue-5.10/mptcp-do-not-drop-partial-packets.patch b/queue-5.10/mptcp-do-not-drop-partial-packets.patch new file mode 100644 index 0000000000..c4bb2f4041 --- /dev/null +++ b/queue-5.10/mptcp-do-not-drop-partial-packets.patch @@ -0,0 +1,77 @@ +From stable+bounces-259295-greg=kroah.com@vger.kernel.org Sun May 31 01:37:58 2026 +From: Sasha Levin +Date: Sat, 30 May 2026 16:07:11 -0400 +Subject: mptcp: do not drop partial packets +To: stable@vger.kernel.org +Cc: Shardul Bankar , Paolo Abeni , "Matthieu Baerts (NGI0)" , Sasha Levin +Message-ID: <20260530200711.3281969-1-sashal@kernel.org> + +From: Shardul Bankar + +[ Upstream commit 50c2d91c5dfa0e465826ec1f8dbad9cdc254bd85 ] + +When a packet arrives with map_seq < ack_seq < end_seq, the beginning +of the packet has already been acknowledged but the end contains new +data. Currently the entire packet is dropped as "old data," forcing +the sender to retransmit. + +Instead, skip the already-acked bytes by adjusting the skb offset and +enqueue only the new portion. Update bytes_received and ack_seq to +reflect the new data consumed. + +A previous attempt at this fix has been sent by Paolo Abeni [1], but had +issues [2]: it also added a zero-window check and changed rcv_wnd_sent +initialization, which caused test regressions. This version addresses +only the partial packet handling without modifying receive window +accounting. + +Fixes: ab174ad8ef76 ("mptcp: move ooo skbs into msk out of order queue.") +Cc: stable@vger.kernel.org +Link: https://lore.kernel.org/c9b426a4e163aa3c4fe8b80c79f1a610f47ae7d8.1763075056.git.pabeni@redhat.com [1] +Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/600 [2] +Signed-off-by: Shardul Bankar +[pabeni@redhat.com: update map] +Signed-off-by: Paolo Abeni +Reviewed-by: Matthieu Baerts (NGI0) +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260515-net-mptcp-misc-fixes-7-1-rc4-v2-1-701e96419f2f@kernel.org +Signed-off-by: Paolo Abeni +[ dropped `msk->bytes_received += copy_len;` and relocated the `drop:` label to the function end for the existing RCVPRUNED `goto drop;` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/mptcp/protocol.c | 22 +++++++++++++++++++--- + 1 file changed, 19 insertions(+), 3 deletions(-) + +--- a/net/mptcp/protocol.c ++++ b/net/mptcp/protocol.c +@@ -314,10 +314,26 @@ static bool __mptcp_move_skb(struct mptc + return false; + } + +- /* old data, keep it simple and drop the whole pkt, sender +- * will retransmit as needed, if needed. ++ /* Completely old data? */ ++ if (!after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) { ++ MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA); ++ mptcp_drop(sk, skb); ++ return false; ++ } ++ ++ /* Partial packet: map_seq < ack_seq < end_seq. ++ * Skip the already-acked bytes and enqueue the new data. + */ +- MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA); ++ copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq; ++ MPTCP_SKB_CB(skb)->offset += msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq; ++ MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq - ++ MPTCP_SKB_CB(skb)->map_seq; ++ WRITE_ONCE(msk->ack_seq, msk->ack_seq + copy_len); ++ ++ skb_set_owner_r(skb, sk); ++ __skb_queue_tail(&sk->sk_receive_queue, skb); ++ return true; ++ + drop: + mptcp_drop(sk, skb); + return false; diff --git a/queue-5.10/mptcp-pm-add_addr-rtx-fix-potential-data-race.patch b/queue-5.10/mptcp-pm-add_addr-rtx-fix-potential-data-race.patch new file mode 100644 index 0000000000..2ea6bf45f7 --- /dev/null +++ b/queue-5.10/mptcp-pm-add_addr-rtx-fix-potential-data-race.patch @@ -0,0 +1,56 @@ +From stable+bounces-249415-greg=kroah.com@vger.kernel.org Tue May 19 06:18:13 2026 +From: Sasha Levin +Date: Mon, 18 May 2026 20:46:04 -0400 +Subject: mptcp: pm: ADD_ADDR rtx: fix potential data-race +To: stable@vger.kernel.org +Cc: "Matthieu Baerts (NGI0)" , Mat Martineau , Jakub Kicinski , Sasha Levin +Message-ID: <20260519004604.1930911-1-sashal@kernel.org> + +From: "Matthieu Baerts (NGI0)" + +[ Upstream commit 5cd6e0ad79d2615264f63929f8b457ad97ae550d ] + +This mptcp_pm_add_timer() helper is executed as a timer callback in +softirq context. To avoid any data races, the socket lock needs to be +held with bh_lock_sock(). + +If the socket is in use, retry again soon after, similar to what is done +with the keepalive timer. + +Fixes: 00cfd77b9063 ("mptcp: retransmit ADD_ADDR when timeout") +Cc: stable@vger.kernel.org +Reviewed-by: Mat Martineau +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260505-net-mptcp-pm-fixes-7-1-rc3-v1-3-fca8091060a4@kernel.org +Signed-off-by: Jakub Kicinski +[ relocated change from net/mptcp/pm.c to net/mptcp/pm_netlink.c ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/mptcp/pm_netlink.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/net/mptcp/pm_netlink.c ++++ b/net/mptcp/pm_netlink.c +@@ -226,6 +226,13 @@ static void mptcp_pm_add_timer(struct ti + if (!entry->addr.id) + return; + ++ bh_lock_sock(sk); ++ if (sock_owned_by_user(sk)) { ++ /* Try again later. */ ++ sk_reset_timer(sk, timer, jiffies + HZ / 20); ++ goto out; ++ } ++ + if (mptcp_pm_should_add_signal(msk)) { + sk_reset_timer(sk, timer, jiffies + TCP_RTO_MAX / 8); + goto out; +@@ -245,6 +252,7 @@ static void mptcp_pm_add_timer(struct ti + spin_unlock_bh(&msk->pm.lock); + + out: ++ bh_unlock_sock(sk); + __sock_put(sk); + } + diff --git a/queue-5.10/mtd-spi-nor-sst-fix-write-enable-before-aai-sequence.patch b/queue-5.10/mtd-spi-nor-sst-fix-write-enable-before-aai-sequence.patch new file mode 100644 index 0000000000..d1b2d74679 --- /dev/null +++ b/queue-5.10/mtd-spi-nor-sst-fix-write-enable-before-aai-sequence.patch @@ -0,0 +1,66 @@ +From stable+bounces-244848-greg=kroah.com@vger.kernel.org Sat May 9 05:29:30 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 19:59:24 -0400 +Subject: mtd: spi-nor: sst: Fix write enable before AAI sequence +To: stable@vger.kernel.org +Cc: Sanjaikumar V S , Hendrik Donner , "Pratyush Yadav (Google)" , Sasha Levin +Message-ID: <20260508235924.2244851-1-sashal@kernel.org> + +From: Sanjaikumar V S + +[ Upstream commit a0f64241d3566a49c0a9b33ba7ae458ae22003a9 ] + +When writing to SST flash starting at an odd address, a single byte is +first programmed using the byte program (BP) command. After this +operation completes, the flash hardware automatically clears the Write +Enable Latch (WEL) bit. + +If an AAI (Auto Address Increment) word program sequence follows, it +requires WEL to be set. Without re-enabling writes, the AAI sequence +fails. + +Add spi_nor_write_enable() after the odd-address byte program when more +data needs to be written. Use a local boolean for clarity. + +Fixes: b199489d37b2 ("mtd: spi-nor: add the framework for SPI NOR") +Cc: stable@vger.kernel.org +Signed-off-by: Sanjaikumar V S +Tested-by: Hendrik Donner +Reviewed-by: Hendrik Donner +Signed-off-by: Pratyush Yadav (Google) +[ kept inline `nor->program_opcode = SPINOR_OP_BP;` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mtd/spi-nor/sst.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/mtd/spi-nor/sst.c ++++ b/drivers/mtd/spi-nor/sst.c +@@ -63,6 +63,8 @@ static int sst_write(struct mtd_info *mt + + /* Start write from odd address. */ + if (to % 2) { ++ bool needs_write_enable = (len > 1); ++ + nor->program_opcode = SPINOR_OP_BP; + + /* write one byte. */ +@@ -76,6 +78,17 @@ static int sst_write(struct mtd_info *mt + + to++; + actual++; ++ ++ /* ++ * Byte program clears the write enable latch. If more ++ * data needs to be written using the AAI sequence, ++ * re-enable writes. ++ */ ++ if (needs_write_enable) { ++ ret = spi_nor_write_enable(nor); ++ if (ret) ++ goto out; ++ } + } + + /* Write out most of the data here. */ diff --git a/queue-5.10/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch b/queue-5.10/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch new file mode 100644 index 0000000000..800ba928ca --- /dev/null +++ b/queue-5.10/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch @@ -0,0 +1,176 @@ +From stable+bounces-243031-greg=kroah.com@vger.kernel.org Mon May 4 18:26:40 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 08:56:17 -0400 +Subject: net: bridge: use a stable FDB dst snapshot in RCU readers +To: stable@vger.kernel.org +Cc: Zhengchuan Liang , stable@kernel.org, Yifan Wu , Juefei Pu , Yuan Tan , Xin Liu , Ren Wei , Ren Wei , Ido Schimmel , Nikolay Aleksandrov , Paolo Abeni , Sasha Levin +Message-ID: <20260504125617.2211840-1-sashal@kernel.org> + +From: Zhengchuan Liang + +[ Upstream commit df4601653201de21b487c3e7fffd464790cab808 ] + +Local FDB entries can be rewritten in place by `fdb_delete_local()`, which +updates `f->dst` to another port or to `NULL` while keeping the entry +alive. Several bridge RCU readers inspect `f->dst`, including +`br_fdb_fillbuf()` through the `brforward_read()` sysfs path. + +These readers currently load `f->dst` multiple times and can therefore +observe inconsistent values across the check and later dereference. +In `br_fdb_fillbuf()`, this means a concurrent local-FDB update can change +`f->dst` after the NULL check and before the `port_no` dereference, +leading to a NULL-ptr-deref. + +Fix this by taking a single `READ_ONCE()` snapshot of `f->dst` in each +affected RCU reader and using that snapshot for the rest of the access +sequence. Also publish the in-place `f->dst` updates in `fdb_delete_local()` +with `WRITE_ONCE()` so the readers and writer use matching access patterns. + +Fixes: 960b589f86c7 ("bridge: Properly check if local fdb entry can be deleted in br_fdb_change_mac_address") +Cc: stable@kernel.org +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Co-developed-by: Yuan Tan +Signed-off-by: Yuan Tan +Suggested-by: Xin Liu +Tested-by: Ren Wei +Signed-off-by: Zhengchuan Liang +Signed-off-by: Ren Wei +Reviewed-by: Ido Schimmel +Acked-by: Nikolay Aleksandrov +Link: https://patch.msgid.link/6570fabb85ecadb8baaf019efe856f407711c7b9.1776043229.git.zcliangcn@gmail.com +Signed-off-by: Paolo Abeni +[ kept combined `BR_PROXYARP_WIFI | BR_NEIGH_SUPPRESS` check and `cb->args[2]` instead of `br_is_neigh_suppress_enabled()` helper and `ctx->fdb_idx` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/bridge/br_arp_nd_proxy.c | 8 +++++--- + net/bridge/br_fdb.c | 28 ++++++++++++++++++---------- + 2 files changed, 23 insertions(+), 13 deletions(-) + +--- a/net/bridge/br_arp_nd_proxy.c ++++ b/net/bridge/br_arp_nd_proxy.c +@@ -199,11 +199,12 @@ void br_do_proxy_suppress_arp(struct sk_ + + f = br_fdb_find_rcu(br, n->ha, vid); + if (f) { ++ const struct net_bridge_port *dst = READ_ONCE(f->dst); + bool replied = false; + + if ((p && (p->flags & BR_PROXYARP)) || +- (f->dst && (f->dst->flags & (BR_PROXYARP_WIFI | +- BR_NEIGH_SUPPRESS)))) { ++ (dst && (dst->flags & (BR_PROXYARP_WIFI | ++ BR_NEIGH_SUPPRESS)))) { + if (!vid) + br_arp_send(br, p, skb->dev, sip, tip, + sha, n->ha, sha, 0, 0); +@@ -463,9 +464,10 @@ void br_do_suppress_nd(struct sk_buff *s + + f = br_fdb_find_rcu(br, n->ha, vid); + if (f) { ++ const struct net_bridge_port *dst = READ_ONCE(f->dst); + bool replied = false; + +- if (f->dst && (f->dst->flags & BR_NEIGH_SUPPRESS)) { ++ if (dst && (dst->flags & BR_NEIGH_SUPPRESS)) { + if (vid != 0) + br_nd_send(br, p, skb, n, + skb->vlan_proto, +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -121,6 +121,7 @@ struct net_device *br_fdb_find_port(cons + const unsigned char *addr, + __u16 vid) + { ++ const struct net_bridge_port *dst; + struct net_bridge_fdb_entry *f; + struct net_device *dev = NULL; + struct net_bridge *br; +@@ -133,8 +134,11 @@ struct net_device *br_fdb_find_port(cons + br = netdev_priv(br_dev); + rcu_read_lock(); + f = br_fdb_find_rcu(br, addr, vid); +- if (f && f->dst) +- dev = f->dst->dev; ++ if (f) { ++ dst = READ_ONCE(f->dst); ++ if (dst) ++ dev = dst->dev; ++ } + rcu_read_unlock(); + + return dev; +@@ -224,7 +228,7 @@ static void fdb_delete_local(struct net_ + vg = nbp_vlan_group(op); + if (op != p && ether_addr_equal(op->dev->dev_addr, addr) && + (!vid || br_vlan_find(vg, vid))) { +- f->dst = op; ++ WRITE_ONCE(f->dst, op); + clear_bit(BR_FDB_ADDED_BY_USER, &f->flags); + return; + } +@@ -235,7 +239,7 @@ static void fdb_delete_local(struct net_ + /* Maybe bridge device has same hw addr? */ + if (p && ether_addr_equal(br->dev->dev_addr, addr) && + (!vid || (v && br_vlan_should_use(v)))) { +- f->dst = NULL; ++ WRITE_ONCE(f->dst, NULL); + clear_bit(BR_FDB_ADDED_BY_USER, &f->flags); + return; + } +@@ -457,6 +461,7 @@ int br_fdb_test_addr(struct net_device * + int br_fdb_fillbuf(struct net_bridge *br, void *buf, + unsigned long maxnum, unsigned long skip) + { ++ const struct net_bridge_port *dst; + struct net_bridge_fdb_entry *f; + struct __fdb_entry *fe = buf; + int num = 0; +@@ -472,7 +477,8 @@ int br_fdb_fillbuf(struct net_bridge *br + continue; + + /* ignore pseudo entry for local MAC address */ +- if (!f->dst) ++ dst = READ_ONCE(f->dst); ++ if (!dst) + continue; + + if (skip) { +@@ -484,8 +490,8 @@ int br_fdb_fillbuf(struct net_bridge *br + memcpy(fe->mac_addr, f->key.addr.addr, ETH_ALEN); + + /* due to ABI compat need to split into hi/lo */ +- fe->port_no = f->dst->port_no; +- fe->port_hi = f->dst->port_no >> 8; ++ fe->port_no = dst->port_no; ++ fe->port_hi = dst->port_no >> 8; + + fe->is_local = test_bit(BR_FDB_LOCAL, &f->flags); + if (!test_bit(BR_FDB_STATIC, &f->flags)) +@@ -775,9 +781,11 @@ int br_fdb_dump(struct sk_buff *skb, + + rcu_read_lock(); + hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) { ++ const struct net_bridge_port *dst = READ_ONCE(f->dst); ++ + if (*idx < cb->args[2]) + goto skip; +- if (filter_dev && (!f->dst || f->dst->dev != filter_dev)) { ++ if (filter_dev && (!dst || dst->dev != filter_dev)) { + if (filter_dev != dev) + goto skip; + /* !f->dst is a special case for bridge +@@ -785,10 +793,10 @@ int br_fdb_dump(struct sk_buff *skb, + * Therefore need a little more filtering + * we only want to dump the !f->dst case + */ +- if (f->dst) ++ if (dst) + goto skip; + } +- if (!filter_dev && f->dst) ++ if (!filter_dev && dst) + goto skip; + + err = fdb_fill_info(skb, br, f, diff --git a/queue-5.10/net-hsr-defer-node-table-free-until-after-rcu-readers.patch b/queue-5.10/net-hsr-defer-node-table-free-until-after-rcu-readers.patch new file mode 100644 index 0000000000..99ff9eacb5 --- /dev/null +++ b/queue-5.10/net-hsr-defer-node-table-free-until-after-rcu-readers.patch @@ -0,0 +1,55 @@ +From stable+bounces-256839-greg=kroah.com@vger.kernel.org Sat May 30 05:53:13 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 20:20:41 -0400 +Subject: net: hsr: defer node table free until after RCU readers +To: stable@vger.kernel.org +Cc: Michael Bommarito , Jakub Kicinski , Sasha Levin +Message-ID: <20260530002041.2171280-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit aaec7096f9961eb223b5b149abe9495525c205d9 ] + +HSR node-list and node-status generic-netlink operations run under +rcu_read_lock(). They walk hsr->node_db through hsr_get_next_node() and +hsr_get_node_data(), but RTM_DELLINK teardown removes the same node table +with plain list_del() and frees each node immediately. + +That lets a generic-netlink reader hold a struct hsr_node pointer across +hsr_dellink(). In a KASAN build, widening the reader window after +hsr_get_next_node() obtains the node reproduces a slab-use-after-free +when the reader copies node->macaddress_A; the freeing stack is +hsr_del_nodes() from hsr_dellink(). + +Use list_del_rcu() and defer the free through the existing +hsr_free_node_rcu() callback. This matches the lifetime rule used by the +HSR prune paths, which already delete nodes with list_del_rcu() and +call_rcu(). + +Fixes: b9a1e627405d ("hsr: implement dellink to clean up resources") +Cc: stable@vger.kernel.org # v5.3+ +Signed-off-by: Michael Bommarito +Link: https://patch.msgid.link/20260513233838.3064715-2-michael.bommarito@gmail.com +Signed-off-by: Jakub Kicinski +[ replaced `list_del`+`call_rcu(hsr_free_node_rcu)` with `list_del_rcu`+`kfree_rcu(node, rcu_head)` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/hsr/hsr_framereg.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/net/hsr/hsr_framereg.c ++++ b/net/hsr/hsr_framereg.c +@@ -123,8 +123,10 @@ void hsr_del_nodes(struct list_head *nod + struct hsr_node *node; + struct hsr_node *tmp; + +- list_for_each_entry_safe(node, tmp, node_db, mac_list) +- kfree(node); ++ list_for_each_entry_safe(node, tmp, node_db, mac_list) { ++ list_del_rcu(&node->mac_list); ++ kfree_rcu(node, rcu_head); ++ } + } + + void prp_handle_san_frame(bool san, enum hsr_port_type port, diff --git a/queue-5.10/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch b/queue-5.10/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch new file mode 100644 index 0000000000..22613f87ab --- /dev/null +++ b/queue-5.10/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch @@ -0,0 +1,96 @@ +From stable+bounces-241121-greg=kroah.com@vger.kernel.org Sat Apr 25 15:37:18 2026 +From: Sasha Levin +Date: Sat, 25 Apr 2026 06:07:11 -0400 +Subject: net/packet: fix TOCTOU race on mmap'd vnet_hdr in tpacket_snd() +To: stable@vger.kernel.org +Cc: Bingquan Chen , Willem de Bruijn , Jakub Kicinski , Sasha Levin +Message-ID: <20260425100711.3493958-1-sashal@kernel.org> + +From: Bingquan Chen + +[ Upstream commit 2c054e17d9d41f1020376806c7f750834ced4dc5 ] + +In tpacket_snd(), when PACKET_VNET_HDR is enabled, vnet_hdr points +directly into the mmap'd TX ring buffer shared with userspace. The +kernel validates the header via __packet_snd_vnet_parse() but then +re-reads all fields later in virtio_net_hdr_to_skb(). A concurrent +userspace thread can modify the vnet_hdr fields between validation +and use, bypassing all safety checks. + +The non-TPACKET path (packet_snd()) already correctly copies vnet_hdr +to a stack-local variable. All other vnet_hdr consumers in the kernel +(tun.c, tap.c, virtio_net.c) also use stack copies. The TPACKET TX +path is the only caller of virtio_net_hdr_to_skb() that reads directly +from user-controlled shared memory. + +Fix this by copying vnet_hdr from the mmap'd ring buffer to a +stack-local variable before validation and use, consistent with the +approach used in packet_snd() and all other callers. + +Fixes: 1d036d25e560 ("packet: tpacket_snd gso and checksum offload") +Signed-off-by: Bingquan Chen +Reviewed-by: Willem de Bruijn +Link: https://patch.msgid.link/20260418112006.78823-1-patzilla007@gmail.com +Signed-off-by: Jakub Kicinski +[ replaced `vnet_hdr_sz` with `sizeof(vnet_hdr)` and `if (vnet_hdr_sz)` with `if (po->has_vnet_hdr)` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/packet/af_packet.c | 25 +++++++++++++++---------- + 1 file changed, 15 insertions(+), 10 deletions(-) + +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -2726,7 +2726,8 @@ static int tpacket_snd(struct packet_soc + { + struct sk_buff *skb = NULL; + struct net_device *dev; +- struct virtio_net_hdr *vnet_hdr = NULL; ++ struct virtio_net_hdr vnet_hdr; ++ bool has_vnet_hdr = false; + struct sockcm_cookie sockc; + __be16 proto; + int err, reserve = 0; +@@ -2826,16 +2827,20 @@ static int tpacket_snd(struct packet_soc + hlen = LL_RESERVED_SPACE(dev); + tlen = dev->needed_tailroom; + if (po->has_vnet_hdr) { +- vnet_hdr = data; +- data += sizeof(*vnet_hdr); +- tp_len -= sizeof(*vnet_hdr); +- if (tp_len < 0 || +- __packet_snd_vnet_parse(vnet_hdr, tp_len)) { ++ data += sizeof(vnet_hdr); ++ tp_len -= sizeof(vnet_hdr); ++ if (tp_len < 0) { ++ tp_len = -EINVAL; ++ goto tpacket_error; ++ } ++ memcpy(&vnet_hdr, data - sizeof(vnet_hdr), sizeof(vnet_hdr)); ++ if (__packet_snd_vnet_parse(&vnet_hdr, tp_len)) { + tp_len = -EINVAL; + goto tpacket_error; + } + copylen = __virtio16_to_cpu(vio_le(), +- vnet_hdr->hdr_len); ++ vnet_hdr.hdr_len); ++ has_vnet_hdr = true; + } + copylen = max_t(int, copylen, dev->hard_header_len); + skb = sock_alloc_send_skb(&po->sk, +@@ -2872,12 +2877,12 @@ tpacket_error: + } + } + +- if (po->has_vnet_hdr) { +- if (virtio_net_hdr_to_skb(skb, vnet_hdr, vio_le())) { ++ if (has_vnet_hdr) { ++ if (virtio_net_hdr_to_skb(skb, &vnet_hdr, vio_le())) { + tp_len = -EINVAL; + goto tpacket_error; + } +- virtio_net_hdr_set_proto(skb, vnet_hdr); ++ virtio_net_hdr_set_proto(skb, &vnet_hdr); + } + + skb->destructor = tpacket_destruct_skb; diff --git a/queue-5.10/net-qrtr-ns-change-servers-radix-tree-to-xarray.patch b/queue-5.10/net-qrtr-ns-change-servers-radix-tree-to-xarray.patch new file mode 100644 index 0000000000..5aaab42559 --- /dev/null +++ b/queue-5.10/net-qrtr-ns-change-servers-radix-tree-to-xarray.patch @@ -0,0 +1,310 @@ +From stable+bounces-242842-greg=kroah.com@vger.kernel.org Mon May 4 11:18:02 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 01:47:51 -0400 +Subject: net: qrtr: ns: Change servers radix tree to xarray +To: stable@vger.kernel.org +Cc: Vignesh Viswanathan , Chris Lew , Simon Horman , "David S. Miller" , Sasha Levin +Message-ID: <20260504054752.1775840-1-sashal@kernel.org> + +From: Vignesh Viswanathan + +[ Upstream commit 608a147a88728f84bbd2efdde3d4984339f1d872 ] + +There is a use after free scenario while iterating through the servers +radix tree despite the ns being a single threaded process. This can +happen when the radix tree APIs are not synchronized with the +rcu_read_lock() APIs. + +Convert the radix tree for servers to xarray to take advantage of the +built in rcu lock usage provided by xarray. + +Signed-off-by: Chris Lew +Signed-off-by: Vignesh Viswanathan +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +Stable-dep-of: 68efba36446a ("net: qrtr: ns: Free the node during ctrl_cmd_bye()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/qrtr/ns.c | 133 ++++++++++------------------------------------------------ + 1 file changed, 24 insertions(+), 109 deletions(-) + +--- a/net/qrtr/ns.c ++++ b/net/qrtr/ns.c +@@ -67,7 +67,7 @@ struct qrtr_server { + + struct qrtr_node { + unsigned int id; +- struct radix_tree_root servers; ++ struct xarray servers; + }; + + /* Max lookup limit is chosen based on the current platform requirements. If the +@@ -89,6 +89,7 @@ static struct qrtr_node *node_get(unsign + return NULL; + + node->id = node_id; ++ xa_init(&node->servers); + + if (radix_tree_insert(&nodes, node_id, node)) { + kfree(node); +@@ -199,40 +200,23 @@ static void lookup_notify(struct sockadd + + static int announce_servers(struct sockaddr_qrtr *sq) + { +- struct radix_tree_iter iter; + struct qrtr_server *srv; + struct qrtr_node *node; +- void __rcu **slot; ++ unsigned long index; + int ret; + + node = node_get(qrtr_ns.local_node); + if (!node) + return 0; + +- rcu_read_lock(); + /* Announce the list of servers registered in this node */ +- radix_tree_for_each_slot(slot, &node->servers, &iter, 0) { +- srv = radix_tree_deref_slot(slot); +- if (!srv) +- continue; +- if (radix_tree_deref_retry(srv)) { +- slot = radix_tree_iter_retry(&iter); +- continue; +- } +- slot = radix_tree_iter_resume(slot, &iter); +- rcu_read_unlock(); +- ++ xa_for_each(&node->servers, index, srv) { + ret = service_announce_new(sq, srv); + if (ret < 0) { + pr_err("failed to announce new service\n"); + return ret; + } +- +- rcu_read_lock(); + } +- +- rcu_read_unlock(); +- + return 0; + } + +@@ -262,14 +246,17 @@ static struct qrtr_server *server_add(un + goto err; + + /* Delete the old server on the same port */ +- old = radix_tree_lookup(&node->servers, port); ++ old = xa_store(&node->servers, port, srv, GFP_KERNEL); + if (old) { +- radix_tree_delete(&node->servers, port); +- kfree(old); ++ if (xa_is_err(old)) { ++ pr_err("failed to add server [0x%x:0x%x] ret:%d\n", ++ srv->service, srv->instance, xa_err(old)); ++ goto err; ++ } else { ++ kfree(old); ++ } + } + +- radix_tree_insert(&node->servers, port, srv); +- + trace_qrtr_ns_server_add(srv->service, srv->instance, + srv->node, srv->port); + +@@ -286,11 +273,11 @@ static int server_del(struct qrtr_node * + struct qrtr_server *srv; + struct list_head *li; + +- srv = radix_tree_lookup(&node->servers, port); ++ srv = xa_load(&node->servers, port); + if (!srv) + return -ENOENT; + +- radix_tree_delete(&node->servers, port); ++ xa_erase(&node->servers, port); + + /* Broadcast the removal of local servers */ + if (srv->node == qrtr_ns.local_node && bcast) +@@ -350,13 +337,12 @@ static int ctrl_cmd_hello(struct sockadd + static int ctrl_cmd_bye(struct sockaddr_qrtr *from) + { + struct qrtr_node *local_node; +- struct radix_tree_iter iter; + struct qrtr_ctrl_pkt pkt; + struct qrtr_server *srv; + struct sockaddr_qrtr sq; + struct msghdr msg = { }; + struct qrtr_node *node; +- void __rcu **slot; ++ unsigned long index; + struct kvec iv; + int ret; + +@@ -367,22 +353,9 @@ static int ctrl_cmd_bye(struct sockaddr_ + if (!node) + return 0; + +- rcu_read_lock(); + /* Advertise removal of this client to all servers of remote node */ +- radix_tree_for_each_slot(slot, &node->servers, &iter, 0) { +- srv = radix_tree_deref_slot(slot); +- if (!srv) +- continue; +- if (radix_tree_deref_retry(srv)) { +- slot = radix_tree_iter_retry(&iter); +- continue; +- } +- slot = radix_tree_iter_resume(slot, &iter); +- rcu_read_unlock(); ++ xa_for_each(&node->servers, index, srv) + server_del(node, srv->port, true); +- rcu_read_lock(); +- } +- rcu_read_unlock(); + + /* Advertise the removal of this client to all local servers */ + local_node = node_get(qrtr_ns.local_node); +@@ -393,18 +366,7 @@ static int ctrl_cmd_bye(struct sockaddr_ + pkt.cmd = cpu_to_le32(QRTR_TYPE_BYE); + pkt.client.node = cpu_to_le32(from->sq_node); + +- rcu_read_lock(); +- radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) { +- srv = radix_tree_deref_slot(slot); +- if (!srv) +- continue; +- if (radix_tree_deref_retry(srv)) { +- slot = radix_tree_iter_retry(&iter); +- continue; +- } +- slot = radix_tree_iter_resume(slot, &iter); +- rcu_read_unlock(); +- ++ xa_for_each(&local_node->servers, index, srv) { + sq.sq_family = AF_QIPCRTR; + sq.sq_node = srv->node; + sq.sq_port = srv->port; +@@ -417,11 +379,7 @@ static int ctrl_cmd_bye(struct sockaddr_ + pr_err("failed to send bye cmd\n"); + return ret; + } +- rcu_read_lock(); + } +- +- rcu_read_unlock(); +- + return 0; + } + +@@ -429,7 +387,6 @@ static int ctrl_cmd_del_client(struct so + unsigned int node_id, unsigned int port) + { + struct qrtr_node *local_node; +- struct radix_tree_iter iter; + struct qrtr_lookup *lookup; + struct qrtr_ctrl_pkt pkt; + struct msghdr msg = { }; +@@ -438,7 +395,7 @@ static int ctrl_cmd_del_client(struct so + struct qrtr_node *node; + struct list_head *tmp; + struct list_head *li; +- void __rcu **slot; ++ unsigned long index; + struct kvec iv; + int ret; + +@@ -484,18 +441,7 @@ static int ctrl_cmd_del_client(struct so + pkt.client.node = cpu_to_le32(node_id); + pkt.client.port = cpu_to_le32(port); + +- rcu_read_lock(); +- radix_tree_for_each_slot(slot, &local_node->servers, &iter, 0) { +- srv = radix_tree_deref_slot(slot); +- if (!srv) +- continue; +- if (radix_tree_deref_retry(srv)) { +- slot = radix_tree_iter_retry(&iter); +- continue; +- } +- slot = radix_tree_iter_resume(slot, &iter); +- rcu_read_unlock(); +- ++ xa_for_each(&local_node->servers, index, srv) { + sq.sq_family = AF_QIPCRTR; + sq.sq_node = srv->node; + sq.sq_port = srv->port; +@@ -508,11 +454,7 @@ static int ctrl_cmd_del_client(struct so + pr_err("failed to send del client cmd\n"); + return ret; + } +- rcu_read_lock(); + } +- +- rcu_read_unlock(); +- + return 0; + } + +@@ -593,13 +535,12 @@ static int ctrl_cmd_del_server(struct so + static int ctrl_cmd_new_lookup(struct sockaddr_qrtr *from, + unsigned int service, unsigned int instance) + { +- struct radix_tree_iter node_iter; + struct qrtr_server_filter filter; +- struct radix_tree_iter srv_iter; + struct qrtr_lookup *lookup; ++ struct qrtr_server *srv; + struct qrtr_node *node; +- void __rcu **node_slot; +- void __rcu **srv_slot; ++ unsigned long node_idx; ++ unsigned long srv_idx; + + /* Accept only local observers */ + if (from->sq_node != qrtr_ns.local_node) +@@ -624,40 +565,14 @@ static int ctrl_cmd_new_lookup(struct so + filter.service = service; + filter.instance = instance; + +- rcu_read_lock(); +- radix_tree_for_each_slot(node_slot, &nodes, &node_iter, 0) { +- node = radix_tree_deref_slot(node_slot); +- if (!node) +- continue; +- if (radix_tree_deref_retry(node)) { +- node_slot = radix_tree_iter_retry(&node_iter); +- continue; +- } +- node_slot = radix_tree_iter_resume(node_slot, &node_iter); +- +- radix_tree_for_each_slot(srv_slot, &node->servers, +- &srv_iter, 0) { +- struct qrtr_server *srv; +- +- srv = radix_tree_deref_slot(srv_slot); +- if (!srv) +- continue; +- if (radix_tree_deref_retry(srv)) { +- srv_slot = radix_tree_iter_retry(&srv_iter); +- continue; +- } +- ++ xa_for_each(&nodes, node_idx, node) { ++ xa_for_each(&node->servers, srv_idx, srv) { + if (!server_match(srv, &filter)) + continue; + +- srv_slot = radix_tree_iter_resume(srv_slot, &srv_iter); +- +- rcu_read_unlock(); + lookup_notify(from, srv, true); +- rcu_read_lock(); + } + } +- rcu_read_unlock(); + + /* Empty notification, to indicate end of listing */ + lookup_notify(from, NULL, true); diff --git a/queue-5.10/net-qrtr-ns-free-the-node-during-ctrl_cmd_bye.patch b/queue-5.10/net-qrtr-ns-free-the-node-during-ctrl_cmd_bye.patch new file mode 100644 index 0000000000..fed6f5e510 --- /dev/null +++ b/queue-5.10/net-qrtr-ns-free-the-node-during-ctrl_cmd_bye.patch @@ -0,0 +1,77 @@ +From stable+bounces-242843-greg=kroah.com@vger.kernel.org Mon May 4 11:18:03 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 01:47:52 -0400 +Subject: net: qrtr: ns: Free the node during ctrl_cmd_bye() +To: stable@vger.kernel.org +Cc: Manivannan Sadhasivam , Jakub Kicinski , Sasha Levin +Message-ID: <20260504054752.1775840-2-sashal@kernel.org> + +From: Manivannan Sadhasivam + +[ Upstream commit 68efba36446a7774ea5b971257ade049272a07ac ] + +A node sends the BYE packet when it is about to go down. So the nameserver +should advertise the removal of the node to all remote and local observers +and free the node finally. But currently, the nameserver doesn't free the +node memory even after processing the BYE packet. This causes the node +memory to leak. + +Hence, remove the node from Xarray list and free the node memory during +both success and failure case of ctrl_cmd_bye(). + +Cc: stable@vger.kernel.org +Fixes: 0c2204a4ad71 ("net: qrtr: Migrate nameservice to kernel from userspace") +Signed-off-by: Manivannan Sadhasivam +Link: https://patch.msgid.link/20260409-qrtr-fix-v3-3-00a8a5ff2b51@oss.qualcomm.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/qrtr/ns.c | 20 +++++++++++++++----- + 1 file changed, 15 insertions(+), 5 deletions(-) + +--- a/net/qrtr/ns.c ++++ b/net/qrtr/ns.c +@@ -344,7 +344,7 @@ static int ctrl_cmd_bye(struct sockaddr_ + struct qrtr_node *node; + unsigned long index; + struct kvec iv; +- int ret; ++ int ret = 0; + + iv.iov_base = &pkt; + iv.iov_len = sizeof(pkt); +@@ -359,8 +359,10 @@ static int ctrl_cmd_bye(struct sockaddr_ + + /* Advertise the removal of this client to all local servers */ + local_node = node_get(qrtr_ns.local_node); +- if (!local_node) +- return 0; ++ if (!local_node) { ++ ret = 0; ++ goto delete_node; ++ } + + memset(&pkt, 0, sizeof(pkt)); + pkt.cmd = cpu_to_le32(QRTR_TYPE_BYE); +@@ -377,10 +379,18 @@ static int ctrl_cmd_bye(struct sockaddr_ + ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt)); + if (ret < 0) { + pr_err("failed to send bye cmd\n"); +- return ret; ++ goto delete_node; + } + } +- return 0; ++ ++ /* Ignore -ENODEV */ ++ ret = 0; ++ ++delete_node: ++ xa_erase(&nodes, from->sq_node); ++ kfree(node); ++ ++ return ret; + } + + static int ctrl_cmd_del_client(struct sockaddr_qrtr *from, diff --git a/queue-5.10/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch b/queue-5.10/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch new file mode 100644 index 0000000000..1c72b904d7 --- /dev/null +++ b/queue-5.10/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch @@ -0,0 +1,95 @@ +From stable+bounces-242837-greg=kroah.com@vger.kernel.org Mon May 4 10:49:20 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 01:19:12 -0400 +Subject: net: qrtr: ns: Limit the maximum number of lookups +To: stable@vger.kernel.org +Cc: Manivannan Sadhasivam , Jakub Kicinski , Sasha Levin +Message-ID: <20260504051912.1723447-1-sashal@kernel.org> + +From: Manivannan Sadhasivam + +[ Upstream commit 5640227d9a21c6a8be249a10677b832e7f40dc55 ] + +Current code does no bound checking on the number of lookups a client can +perform. Though the code restricts the lookups to local clients, there is +still a possibility of a malicious local client sending a flood of +NEW_LOOKUP messages over the same socket. + +Fix this issue by limiting the maximum number of lookups to 64 globally. +Since the nameserver allows only atmost one local observer, this global +lookup count will ensure that the lookups stay within the limit. + +Note that, limit of 64 is chosen based on the current platform +requirements. If requirement changes in the future, this limit can be +increased. + +Cc: stable@vger.kernel.org +Fixes: 0c2204a4ad71 ("net: qrtr: Migrate nameservice to kernel from userspace") +Signed-off-by: Manivannan Sadhasivam +Link: https://patch.msgid.link/20260409-qrtr-fix-v3-2-00a8a5ff2b51@oss.qualcomm.com +Signed-off-by: Jakub Kicinski +[ adapted comment block to only mention QRTR_NS_MAX_LOOKUPS and kept kzalloc() instead of kzalloc_obj() due to missing prerequisite commits ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/qrtr/ns.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/net/qrtr/ns.c ++++ b/net/qrtr/ns.c +@@ -21,6 +21,7 @@ static struct { + struct socket *sock; + struct sockaddr_qrtr bcast_sq; + struct list_head lookups; ++ u32 lookup_count; + struct workqueue_struct *workqueue; + struct work_struct work; + void (*saved_data_ready)(struct sock *sk); +@@ -69,6 +70,11 @@ struct qrtr_node { + struct radix_tree_root servers; + }; + ++/* Max lookup limit is chosen based on the current platform requirements. If the ++ * requirement changes in the future, this value can be increased. ++ */ ++#define QRTR_NS_MAX_LOOKUPS 64 ++ + static struct qrtr_node *node_get(unsigned int node_id) + { + struct qrtr_node *node; +@@ -457,6 +463,7 @@ static int ctrl_cmd_del_client(struct so + + list_del(&lookup->li); + kfree(lookup); ++ qrtr_ns.lookup_count--; + } + + /* Remove the server belonging to this port but don't broadcast +@@ -598,6 +605,11 @@ static int ctrl_cmd_new_lookup(struct so + if (from->sq_node != qrtr_ns.local_node) + return -EINVAL; + ++ if (qrtr_ns.lookup_count >= QRTR_NS_MAX_LOOKUPS) { ++ pr_err_ratelimited("QRTR client node exceeds max lookup limit!\n"); ++ return -ENOSPC; ++ } ++ + lookup = kzalloc(sizeof(*lookup), GFP_KERNEL); + if (!lookup) + return -ENOMEM; +@@ -606,6 +618,7 @@ static int ctrl_cmd_new_lookup(struct so + lookup->service = service; + lookup->instance = instance; + list_add_tail(&lookup->li, &qrtr_ns.lookups); ++ qrtr_ns.lookup_count++; + + memset(&filter, 0, sizeof(filter)); + filter.service = service; +@@ -672,6 +685,7 @@ static void ctrl_cmd_del_lookup(struct s + + list_del(&lookup->li); + kfree(lookup); ++ qrtr_ns.lookup_count--; + } + } + diff --git a/queue-5.10/net-qrtr-ns-limit-the-total-number-of-nodes.patch b/queue-5.10/net-qrtr-ns-limit-the-total-number-of-nodes.patch new file mode 100644 index 0000000000..dc16936410 --- /dev/null +++ b/queue-5.10/net-qrtr-ns-limit-the-total-number-of-nodes.patch @@ -0,0 +1,72 @@ +From stable+bounces-242997-greg=kroah.com@vger.kernel.org Mon May 4 17:02:48 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 07:32:41 -0400 +Subject: net: qrtr: ns: Limit the total number of nodes +To: stable@vger.kernel.org +Cc: Manivannan Sadhasivam , Jakub Kicinski , Sasha Levin +Message-ID: <20260504113241.2090164-1-sashal@kernel.org> + +From: Manivannan Sadhasivam + +[ Upstream commit 27d5e84e810b0849d08b9aec68e48570461ce313 ] + +Currently, the nameserver doesn't limit the number of nodes it handles. +This can be an attack vector if a malicious client starts registering +random nodes, leading to memory exhaustion. + +Hence, limit the maximum number of nodes to 64. Note that, limit of 64 is +chosen based on the current platform requirements. If requirement changes +in the future, this limit can be increased. + +Cc: stable@vger.kernel.org +Fixes: 0c2204a4ad71 ("net: qrtr: Migrate nameservice to kernel from userspace") +Signed-off-by: Manivannan Sadhasivam +Link: https://patch.msgid.link/20260409-qrtr-fix-v3-4-00a8a5ff2b51@oss.qualcomm.com +Signed-off-by: Jakub Kicinski +[ dropped node_count-- hunk since ctrl_cmd_bye() has no delete_node ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/qrtr/ns.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +--- a/net/qrtr/ns.c ++++ b/net/qrtr/ns.c +@@ -75,6 +75,16 @@ struct qrtr_node { + */ + #define QRTR_NS_MAX_LOOKUPS 64 + ++/* Max nodes, server, lookup limits are chosen based on the current platform ++ * requirements. If the requirement changes in the future, these values can be ++ * increased. ++ */ ++#define QRTR_NS_MAX_NODES 64 ++#define QRTR_NS_MAX_SERVERS 256 ++#define QRTR_NS_MAX_LOOKUPS 64 ++ ++static u8 node_count; ++ + static struct qrtr_node *node_get(unsigned int node_id) + { + struct qrtr_node *node; +@@ -83,6 +93,11 @@ static struct qrtr_node *node_get(unsign + if (node) + return node; + ++ if (node_count >= QRTR_NS_MAX_NODES) { ++ pr_err_ratelimited("QRTR clients exceed max node limit!\n"); ++ return NULL; ++ } ++ + /* If node didn't exist, allocate and insert it to the tree */ + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) +@@ -96,6 +111,8 @@ static struct qrtr_node *node_get(unsign + return NULL; + } + ++ node_count++; ++ + return node; + } + diff --git a/queue-5.10/net-remove-redundant-if-statements.patch b/queue-5.10/net-remove-redundant-if-statements.patch new file mode 100644 index 0000000000..3494838bca --- /dev/null +++ b/queue-5.10/net-remove-redundant-if-statements.patch @@ -0,0 +1,752 @@ +From stable+bounces-256828-greg=kroah.com@vger.kernel.org Sat May 30 05:19:52 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 19:49:41 -0400 +Subject: net: Remove redundant if statements +To: stable@vger.kernel.org +Cc: Yajun Deng , "David S. Miller" , Sasha Levin +Message-ID: <20260529234942.1939638-1-sashal@kernel.org> + +From: Yajun Deng + +[ Upstream commit 1160dfa178eb848327e9dec39960a735f4dc1685 ] + +The 'if (dev)' statement already move into dev_{put , hold}, so remove +redundant if statements. + +Signed-off-by: Yajun Deng +Signed-off-by: David S. Miller +Stable-dep-of: e196115ec330 ("netfilter: nf_queue: hold bridge skb->dev while queued") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/batman-adv/bridge_loop_avoidance.c | 6 ++---- + net/batman-adv/distributed-arp-table.c | 3 +-- + net/batman-adv/gateway_client.c | 3 +-- + net/batman-adv/multicast.c | 9 +++------ + net/batman-adv/originator.c | 12 ++++-------- + net/batman-adv/translation-table.c | 9 +++------ + net/can/raw.c | 8 ++------ + net/core/dev.c | 6 ++---- + net/core/drop_monitor.c | 6 ++---- + net/core/dst.c | 6 ++---- + net/core/neighbour.c | 15 +++++---------- + net/ethtool/netlink.c | 6 ++---- + net/ieee802154/nl-phy.c | 3 +-- + net/ieee802154/nl802154.c | 3 +-- + net/ieee802154/socket.c | 3 +-- + net/ipv4/fib_semantics.c | 4 +--- + net/ipv4/route.c | 3 +-- + net/ipv6/addrconf.c | 6 ++---- + net/ipv6/ip6mr.c | 3 +-- + net/ipv6/route.c | 3 +-- + net/netfilter/nf_queue.c | 24 ++++++++---------------- + net/netlabel/netlabel_unlabeled.c | 6 ++---- + net/netrom/nr_loopback.c | 3 +-- + net/netrom/nr_route.c | 3 +-- + net/packet/af_packet.c | 15 +++++---------- + net/phonet/af_phonet.c | 3 +-- + net/phonet/pn_dev.c | 6 ++---- + net/phonet/socket.c | 3 +-- + net/sched/act_mirred.c | 6 ++---- + net/smc/smc_pnet.c | 3 +-- + net/wireless/nl80211.c | 16 +++++----------- + net/wireless/scan.c | 3 +-- + 32 files changed, 68 insertions(+), 140 deletions(-) + +--- a/net/batman-adv/bridge_loop_avoidance.c ++++ b/net/batman-adv/bridge_loop_avoidance.c +@@ -2402,8 +2402,7 @@ out: + if (primary_if) + batadv_hardif_put(primary_if); + +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + return ret; + } +@@ -2640,8 +2639,7 @@ out: + if (primary_if) + batadv_hardif_put(primary_if); + +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + return ret; + } +--- a/net/batman-adv/distributed-arp-table.c ++++ b/net/batman-adv/distributed-arp-table.c +@@ -1045,8 +1045,7 @@ out: + if (primary_if) + batadv_hardif_put(primary_if); + +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + return ret; + } +--- a/net/batman-adv/gateway_client.c ++++ b/net/batman-adv/gateway_client.c +@@ -590,8 +590,7 @@ int batadv_gw_dump(struct sk_buff *msg, + out: + if (primary_if) + batadv_hardif_put(primary_if); +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + return ret; + } +--- a/net/batman-adv/multicast.c ++++ b/net/batman-adv/multicast.c +@@ -92,8 +92,7 @@ static struct net_device *batadv_mcast_g + upper = netdev_master_upper_dev_get_rcu(upper); + } while (upper && !(upper->priv_flags & IFF_EBRIDGE)); + +- if (upper) +- dev_hold(upper); ++ dev_hold(upper); + rcu_read_unlock(); + + return upper; +@@ -541,8 +540,7 @@ batadv_mcast_mla_softif_get(struct net_d + } + + out: +- if (bridge) +- dev_put(bridge); ++ dev_put(bridge); + + return ret4 + ret6; + } +@@ -2386,8 +2384,7 @@ batadv_mcast_netlink_get_primary(struct + } + + out: +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + if (!ret && primary_if) + *primary_if = hard_iface; +--- a/net/batman-adv/originator.c ++++ b/net/batman-adv/originator.c +@@ -823,12 +823,10 @@ int batadv_hardif_neigh_dump(struct sk_b + out: + if (hardif) + batadv_hardif_put(hardif); +- if (hard_iface) +- dev_put(hard_iface); ++ dev_put(hard_iface); + if (primary_if) + batadv_hardif_put(primary_if); +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + return ret; + } +@@ -1502,12 +1500,10 @@ int batadv_orig_dump(struct sk_buff *msg + out: + if (hardif) + batadv_hardif_put(hardif); +- if (hard_iface) +- dev_put(hard_iface); ++ dev_put(hard_iface); + if (primary_if) + batadv_hardif_put(primary_if); +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + return ret; + } +--- a/net/batman-adv/translation-table.c ++++ b/net/batman-adv/translation-table.c +@@ -815,8 +815,7 @@ check_roaming: + out: + if (in_hardif) + batadv_hardif_put(in_hardif); +- if (in_dev) +- dev_put(in_dev); ++ dev_put(in_dev); + if (tt_local) + batadv_tt_local_entry_put(tt_local); + if (tt_global) +@@ -1331,8 +1330,7 @@ int batadv_tt_local_dump(struct sk_buff + out: + if (primary_if) + batadv_hardif_put(primary_if); +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + cb->args[0] = bucket; + cb->args[1] = idx; +@@ -2252,8 +2250,7 @@ int batadv_tt_global_dump(struct sk_buff + out: + if (primary_if) + batadv_hardif_put(primary_if); +- if (soft_iface) +- dev_put(soft_iface); ++ dev_put(soft_iface); + + cb->args[0] = bucket; + cb->args[1] = idx; +--- a/net/can/raw.c ++++ b/net/can/raw.c +@@ -601,9 +601,7 @@ static int raw_setsockopt(struct socket + ro->count = count; + + out_fil: +- if (dev) +- dev_put(dev); +- ++ dev_put(dev); + release_sock(sk); + rtnl_unlock(); + +@@ -647,9 +645,7 @@ static int raw_setsockopt(struct socket + ro->err_mask = err_mask; + + out_err: +- if (dev) +- dev_put(dev); +- ++ dev_put(dev); + release_sock(sk); + rtnl_unlock(); + +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -906,8 +906,7 @@ struct net_device *dev_get_by_name(struc + + rcu_read_lock(); + dev = dev_get_by_name_rcu(net, name); +- if (dev) +- dev_hold(dev); ++ dev_hold(dev); + rcu_read_unlock(); + return dev; + } +@@ -980,8 +979,7 @@ struct net_device *dev_get_by_index(stru + + rcu_read_lock(); + dev = dev_get_by_index_rcu(net, ifindex); +- if (dev) +- dev_hold(dev); ++ dev_hold(dev); + rcu_read_unlock(); + return dev; + } +--- a/net/core/drop_monitor.c ++++ b/net/core/drop_monitor.c +@@ -854,8 +854,7 @@ net_dm_hw_metadata_copy(const struct dev + } + + hw_metadata->input_dev = metadata->input_dev; +- if (hw_metadata->input_dev) +- dev_hold(hw_metadata->input_dev); ++ dev_hold(hw_metadata->input_dev); + + return hw_metadata; + +@@ -871,8 +870,7 @@ free_hw_metadata: + static void + net_dm_hw_metadata_free(const struct devlink_trap_metadata *hw_metadata) + { +- if (hw_metadata->input_dev) +- dev_put(hw_metadata->input_dev); ++ dev_put(hw_metadata->input_dev); + kfree(hw_metadata->fa_cookie); + kfree(hw_metadata->trap_name); + kfree(hw_metadata->trap_group_name); +--- a/net/core/dst.c ++++ b/net/core/dst.c +@@ -49,8 +49,7 @@ void dst_init(struct dst_entry *dst, str + unsigned short flags) + { + dst->dev = dev; +- if (dev) +- dev_hold(dev); ++ dev_hold(dev); + dst->ops = ops; + dst_init_metrics(dst, dst_default_metrics.metrics, true); + dst->expires = 0UL; +@@ -111,8 +110,7 @@ struct dst_entry *dst_destroy(struct dst + #endif + if (dst->ops->destroy) + dst->ops->destroy(dst); +- if (dst->dev) +- dev_put(dst->dev); ++ dev_put(dst->dev); + + lwtstate_put(dst->lwtstate); + +--- a/net/core/neighbour.c ++++ b/net/core/neighbour.c +@@ -743,12 +743,10 @@ struct pneigh_entry * pneigh_lookup(stru + write_pnet(&n->net, net); + memcpy(n->key, pkey, key_len); + n->dev = dev; +- if (dev) +- dev_hold(dev); ++ dev_hold(dev); + + if (tbl->pconstructor && tbl->pconstructor(n)) { +- if (dev) +- dev_put(dev); ++ dev_put(dev); + kfree(n); + n = NULL; + goto out; +@@ -780,8 +778,7 @@ int pneigh_delete(struct neigh_table *tb + write_unlock_bh(&tbl->lock); + if (tbl->pdestructor) + tbl->pdestructor(n); +- if (n->dev) +- dev_put(n->dev); ++ dev_put(n->dev); + kfree(n); + return 0; + } +@@ -814,8 +811,7 @@ static int pneigh_ifdown_and_unlock(stru + n->next = NULL; + if (tbl->pdestructor) + tbl->pdestructor(n); +- if (n->dev) +- dev_put(n->dev); ++ dev_put(n->dev); + kfree(n); + } + return -ENOENT; +@@ -1677,8 +1673,7 @@ void neigh_parms_release(struct neigh_ta + list_del(&parms->list); + parms->dead = 1; + write_unlock_bh(&tbl->lock); +- if (parms->dev) +- dev_put(parms->dev); ++ dev_put(parms->dev); + call_rcu(&parms->rcu_head, neigh_rcu_free_parms); + } + EXPORT_SYMBOL(neigh_parms_release); +--- a/net/ethtool/netlink.c ++++ b/net/ethtool/netlink.c +@@ -356,8 +356,7 @@ static int ethnl_default_doit(struct sk_ + ops->cleanup_data(reply_data); + + genlmsg_end(rskb, reply_payload); +- if (req_info->dev) +- dev_put(req_info->dev); ++ dev_put(req_info->dev); + kfree(reply_data); + kfree(req_info); + return genlmsg_reply(rskb, info); +@@ -369,8 +368,7 @@ err_cleanup: + if (ops->cleanup_data) + ops->cleanup_data(reply_data); + err_dev: +- if (req_info->dev) +- dev_put(req_info->dev); ++ dev_put(req_info->dev); + kfree(reply_data); + kfree(req_info); + return ret; +--- a/net/ieee802154/nl-phy.c ++++ b/net/ieee802154/nl-phy.c +@@ -340,8 +340,7 @@ nla_put_failure: + out_dev: + wpan_phy_put(phy); + out: +- if (dev) +- dev_put(dev); ++ dev_put(dev); + + return rc; + } +--- a/net/ieee802154/nl802154.c ++++ b/net/ieee802154/nl802154.c +@@ -2230,8 +2230,7 @@ static void nl802154_post_doit(const str + if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { + struct wpan_dev *wpan_dev = info->user_ptr[1]; + +- if (wpan_dev->netdev) +- dev_put(wpan_dev->netdev); ++ dev_put(wpan_dev->netdev); + } else { + dev_put(info->user_ptr[1]); + } +--- a/net/ieee802154/socket.c ++++ b/net/ieee802154/socket.c +@@ -41,8 +41,7 @@ ieee802154_get_dev(struct net *net, cons + ieee802154_devaddr_to_raw(hwaddr, addr->extended_addr); + rcu_read_lock(); + dev = dev_getbyhwaddr_rcu(net, ARPHRD_IEEE802154, hwaddr); +- if (dev) +- dev_hold(dev); ++ dev_hold(dev); + rcu_read_unlock(); + break; + case IEEE802154_ADDR_SHORT: +--- a/net/ipv4/fib_semantics.c ++++ b/net/ipv4/fib_semantics.c +@@ -210,9 +210,7 @@ static void rt_fibinfo_free_cpus(struct + + void fib_nh_common_release(struct fib_nh_common *nhc) + { +- if (nhc->nhc_dev) +- dev_put(nhc->nhc_dev); +- ++ dev_put(nhc->nhc_dev); + lwtstate_put(nhc->nhc_lwtstate); + rt_fibinfo_free_cpus(nhc->nhc_pcpu_rth_output); + rt_fibinfo_free(&nhc->nhc_rth_input); +--- a/net/ipv4/route.c ++++ b/net/ipv4/route.c +@@ -2782,8 +2782,7 @@ struct dst_entry *ipv4_blackhole_route(s + new->output = dst_discard_out; + + new->dev = net->loopback_dev; +- if (new->dev) +- dev_hold(new->dev); ++ dev_hold(new->dev); + + rt->rt_is_input = ort->rt_is_input; + rt->rt_iif = ort->rt_iif; +--- a/net/ipv6/addrconf.c ++++ b/net/ipv6/addrconf.c +@@ -693,8 +693,7 @@ static int inet6_netconf_get_devconf(str + errout: + if (in6_dev) + in6_dev_put(in6_dev); +- if (dev) +- dev_put(dev); ++ dev_put(dev); + return err; + } + +@@ -5469,8 +5468,7 @@ static int inet6_rtm_getaddr(struct sk_b + errout_ifa: + in6_ifa_put(ifa); + errout: +- if (dev) +- dev_put(dev); ++ dev_put(dev); + if (fillargs.netnsid >= 0) + put_net(tgt_net); + +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -561,8 +561,7 @@ static int pim6_rcv(struct sk_buff *skb) + read_lock(&mrt_lock); + if (reg_vif_num >= 0) + reg_dev = mrt->vif_table[reg_vif_num].dev; +- if (reg_dev) +- dev_hold(reg_dev); ++ dev_hold(reg_dev); + read_unlock(&mrt_lock); + + if (!reg_dev) +--- a/net/ipv6/route.c ++++ b/net/ipv6/route.c +@@ -3521,8 +3521,7 @@ out: + fib_nh_common_release(&fib6_nh->nh_common); + fib6_nh->nh_common.nhc_pcpu_rth_output = NULL; + fib6_nh->fib_nh_lws = NULL; +- if (dev) +- dev_put(dev); ++ dev_put(dev); + } + + return err; +--- a/net/netfilter/nf_queue.c ++++ b/net/netfilter/nf_queue.c +@@ -60,18 +60,14 @@ static void nf_queue_entry_release_refs( + struct nf_hook_state *state = &entry->state; + + /* Release those devices we held, or Alexey will kill me. */ +- if (state->in) +- dev_put(state->in); +- if (state->out) +- dev_put(state->out); ++ dev_put(state->in); ++ dev_put(state->out); + if (state->sk) + nf_queue_sock_put(state->sk); + + #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) +- if (entry->physin) +- dev_put(entry->physin); +- if (entry->physout) +- dev_put(entry->physout); ++ dev_put(entry->physin); ++ dev_put(entry->physout); + #endif + } + +@@ -107,16 +103,12 @@ bool nf_queue_entry_get_refs(struct nf_q + if (state->sk && !refcount_inc_not_zero(&state->sk->sk_refcnt)) + return false; + +- if (state->in) +- dev_hold(state->in); +- if (state->out) +- dev_hold(state->out); ++ dev_hold(state->in); ++ dev_hold(state->out); + + #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) +- if (entry->physin) +- dev_hold(entry->physin); +- if (entry->physout) +- dev_hold(entry->physout); ++ dev_hold(entry->physin); ++ dev_hold(entry->physout); + #endif + return true; + } +--- a/net/netlabel/netlabel_unlabeled.c ++++ b/net/netlabel/netlabel_unlabeled.c +@@ -492,8 +492,7 @@ static int netlbl_unlhsh_remove_addr4(st + netlbl_af4list_audit_addr(audit_buf, 1, + (dev != NULL ? dev->name : NULL), + addr->s_addr, mask->s_addr); +- if (dev != NULL) +- dev_put(dev); ++ dev_put(dev); + if (entry != NULL && + security_secid_to_secctx(entry->secid, + &secctx, &secctx_len) == 0) { +@@ -553,8 +552,7 @@ static int netlbl_unlhsh_remove_addr6(st + netlbl_af6list_audit_addr(audit_buf, 1, + (dev != NULL ? dev->name : NULL), + addr, mask); +- if (dev != NULL) +- dev_put(dev); ++ dev_put(dev); + if (entry != NULL && + security_secid_to_secctx(entry->secid, + &secctx, &secctx_len) == 0) { +--- a/net/netrom/nr_loopback.c ++++ b/net/netrom/nr_loopback.c +@@ -59,8 +59,7 @@ static void nr_loopback_timer(struct tim + if (dev == NULL || nr_rx_frame(skb, dev) == 0) + kfree_skb(skb); + +- if (dev != NULL) +- dev_put(dev); ++ dev_put(dev); + + if (!skb_queue_empty(&loopback_queue) && !nr_loopback_running()) + mod_timer(&loopback_timer, jiffies + 10); +--- a/net/netrom/nr_route.c ++++ b/net/netrom/nr_route.c +@@ -573,8 +573,7 @@ struct net_device *nr_dev_first(void) + if (first == NULL || strncmp(dev->name, first->name, 3) < 0) + first = dev; + } +- if (first) +- dev_hold(first); ++ dev_hold(first); + rcu_read_unlock(); + + return first; +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -249,8 +249,7 @@ static struct net_device *packet_cached_ + + rcu_read_lock(); + dev = rcu_dereference(po->cached_dev); +- if (likely(dev)) +- dev_hold(dev); ++ dev_hold(dev); + rcu_read_unlock(); + + return dev; +@@ -3103,8 +3102,7 @@ static int packet_snd(struct socket *soc + out_free: + kfree_skb(skb); + out_unlock: +- if (dev) +- dev_put(dev); ++ dev_put(dev); + out: + return err; + } +@@ -3241,8 +3239,7 @@ static int packet_do_bind(struct sock *s + } + } + +- if (dev) +- dev_hold(dev); ++ dev_hold(dev); + + proto_curr = po->prot_hook.type; + dev_curr = po->prot_hook.dev; +@@ -3279,8 +3276,7 @@ static int packet_do_bind(struct sock *s + packet_cached_dev_assign(po, dev); + } + } +- if (dev_curr) +- dev_put(dev_curr); ++ dev_put(dev_curr); + + if (proto == 0 || !need_rehook) + goto out_unlock; +@@ -4216,8 +4212,7 @@ static int packet_notifier(struct notifi + if (msg == NETDEV_UNREGISTER) { + packet_cached_dev_reset(po); + WRITE_ONCE(po->ifindex, -1); +- if (po->prot_hook.dev) +- dev_put(po->prot_hook.dev); ++ dev_put(po->prot_hook.dev); + po->prot_hook.dev = NULL; + } + spin_unlock(&po->bind_lock); +--- a/net/phonet/af_phonet.c ++++ b/net/phonet/af_phonet.c +@@ -275,8 +275,7 @@ int pn_skb_send(struct sock *sk, struct + + drop: + kfree_skb(skb); +- if (dev) +- dev_put(dev); ++ dev_put(dev); + return err; + } + EXPORT_SYMBOL(pn_skb_send); +--- a/net/phonet/pn_dev.c ++++ b/net/phonet/pn_dev.c +@@ -122,8 +122,7 @@ struct net_device *phonet_device_get(str + break; + dev = NULL; + } +- if (dev) +- dev_hold(dev); ++ dev_hold(dev); + rcu_read_unlock(); + return dev; + } +@@ -411,8 +410,7 @@ struct net_device *phonet_route_output(s + daddr >>= 2; + rcu_read_lock(); + dev = rcu_dereference(routes->table[daddr]); +- if (dev) +- dev_hold(dev); ++ dev_hold(dev); + rcu_read_unlock(); + + if (!dev) +--- a/net/phonet/socket.c ++++ b/net/phonet/socket.c +@@ -379,8 +379,7 @@ static int pn_socket_ioctl(struct socket + saddr = PN_NO_ADDR; + release_sock(sk); + +- if (dev) +- dev_put(dev); ++ dev_put(dev); + if (saddr == PN_NO_ADDR) + return -EHOSTUNREACH; + +--- a/net/sched/act_mirred.c ++++ b/net/sched/act_mirred.c +@@ -79,8 +79,7 @@ static void tcf_mirred_release(struct tc + + /* last reference to action, no need to lock */ + dev = rcu_dereference_protected(m->tcfm_dev, 1); +- if (dev) +- dev_put(dev); ++ dev_put(dev); + } + + static const struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = { +@@ -181,8 +180,7 @@ static int tcf_mirred_init(struct net *n + mac_header_xmit = dev_is_mac_header_xmit(dev); + dev = rcu_replace_pointer(m->tcfm_dev, dev, + lockdep_is_held(&m->tcf_lock)); +- if (dev) +- dev_put(dev); ++ dev_put(dev); + m->tcfm_mac_header_xmit = mac_header_xmit; + } + goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); +--- a/net/smc/smc_pnet.c ++++ b/net/smc/smc_pnet.c +@@ -395,8 +395,7 @@ static int smc_pnet_add_eth(struct smc_p + return 0; + + out_put: +- if (ndev) +- dev_put(ndev); ++ dev_put(ndev); + return rc; + } + +--- a/net/wireless/nl80211.c ++++ b/net/wireless/nl80211.c +@@ -6356,8 +6356,7 @@ static int nl80211_set_station(struct sk + err = rdev_change_station(rdev, dev, mac_addr, ¶ms); + + out_put_vlan: +- if (params.vlan) +- dev_put(params.vlan); ++ dev_put(params.vlan); + + return err; + } +@@ -6592,8 +6591,7 @@ static int nl80211_new_station(struct sk + + err = rdev_add_station(rdev, dev, mac_addr, ¶ms); + +- if (params.vlan) +- dev_put(params.vlan); ++ dev_put(params.vlan); + return err; + } + +@@ -8308,8 +8306,7 @@ static int nl80211_trigger_scan(struct s + goto out_free; + + nl80211_send_scan_start(rdev, wdev); +- if (wdev->netdev) +- dev_hold(wdev->netdev); ++ dev_hold(wdev->netdev); + + return 0; + +@@ -14661,9 +14658,7 @@ static int nl80211_pre_doit(const struct + return -ENETDOWN; + } + +- if (dev) +- dev_hold(dev); +- ++ dev_hold(dev); + info->user_ptr[0] = rdev; + } + +@@ -14677,8 +14672,7 @@ static void nl80211_post_doit(const stru + if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) { + struct wireless_dev *wdev = info->user_ptr[1]; + +- if (wdev->netdev) +- dev_put(wdev->netdev); ++ dev_put(wdev->netdev); + } else { + dev_put(info->user_ptr[1]); + } +--- a/net/wireless/scan.c ++++ b/net/wireless/scan.c +@@ -1057,8 +1057,7 @@ void ___cfg80211_scan_done(struct cfg802 + } + #endif + +- if (wdev->netdev) +- dev_put(wdev->netdev); ++ dev_put(wdev->netdev); + + kfree(rdev->int_scan_req); + rdev->int_scan_req = NULL; diff --git a/queue-5.10/netfilter-nf_queue-hold-bridge-skb-dev-while-queued.patch b/queue-5.10/netfilter-nf_queue-hold-bridge-skb-dev-while-queued.patch new file mode 100644 index 0000000000..a9b30ce4a5 --- /dev/null +++ b/queue-5.10/netfilter-nf_queue-hold-bridge-skb-dev-while-queued.patch @@ -0,0 +1,94 @@ +From stable+bounces-256829-greg=kroah.com@vger.kernel.org Sat May 30 05:19:54 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 19:49:42 -0400 +Subject: netfilter: nf_queue: hold bridge skb->dev while queued +To: stable@vger.kernel.org +Cc: Haoze Xie , stable@kernel.org, Yuan Tan , Yifan Wu , Juefei Pu , Xin Liu , Ren Wei , Pablo Neira Ayuso , Sasha Levin +Message-ID: <20260529234942.1939638-2-sashal@kernel.org> + +From: Haoze Xie + +[ Upstream commit e196115ec330a18de415bdb9f5071aa9f08e53ce ] + +br_pass_frame_up() rewrites skb->dev from the ingress port to the bridge +master before queueing bridge LOCAL_IN packets. NFQUEUE only holds +references on state.in/out and bridge physdevs, so a queued bridge +packet can retain a freed bridge master in skb->dev until reinjection. + +When the verdict is reinjected later, br_netif_receive_skb() re-enters +the receive path with skb->dev still pointing at the freed bridge master, +triggering a use-after-free. + +Store skb->dev in the queue entry, hold a reference on it for the queue +lifetime, and use the saved device when dropping queued packets during +NETDEV_DOWN handling. + +Fixes: ac2863445686 ("netfilter: bridge: add nf_afinfo to enable queuing to userspace") +Cc: stable@kernel.org +Reported-by: Yuan Tan +Reported-by: Yifan Wu +Reported-by: Juefei Pu +Reported-by: Xin Liu +Signed-off-by: Haoze Xie +Signed-off-by: Ren Wei +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/net/netfilter/nf_queue.h | 1 + + net/netfilter/nf_queue.c | 4 +++- + net/netfilter/nfnetlink_queue.c | 2 ++ + 3 files changed, 6 insertions(+), 1 deletion(-) + +--- a/include/net/netfilter/nf_queue.h ++++ b/include/net/netfilter/nf_queue.h +@@ -12,6 +12,7 @@ + struct nf_queue_entry { + struct list_head list; + struct sk_buff *skb; ++ struct net_device *skb_dev; + unsigned int id; + unsigned int hook_index; /* index in hook_entries->hook[] */ + #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) +--- a/net/netfilter/nf_queue.c ++++ b/net/netfilter/nf_queue.c +@@ -60,6 +60,7 @@ static void nf_queue_entry_release_refs( + struct nf_hook_state *state = &entry->state; + + /* Release those devices we held, or Alexey will kill me. */ ++ dev_put(entry->skb_dev); + dev_put(state->in); + dev_put(state->out); + if (state->sk) +@@ -103,6 +104,7 @@ bool nf_queue_entry_get_refs(struct nf_q + if (state->sk && !refcount_inc_not_zero(&state->sk->sk_refcnt)) + return false; + ++ dev_hold(entry->skb_dev); + dev_hold(state->in); + dev_hold(state->out); + +@@ -204,11 +206,11 @@ static int __nf_queue(struct sk_buff *sk + + *entry = (struct nf_queue_entry) { + .skb = skb, ++ .skb_dev = skb->dev, + .state = *state, + .hook_index = index, + .size = sizeof(*entry) + route_key_size, + }; +- + __nf_queue_entry_init_physdevs(entry); + + if (!nf_queue_entry_get_refs(entry)) { +--- a/net/netfilter/nfnetlink_queue.c ++++ b/net/netfilter/nfnetlink_queue.c +@@ -911,6 +911,8 @@ dev_cmp(struct nf_queue_entry *entry, un + if (physinif == ifindex || physoutif == ifindex) + return 1; + #endif ++ if (entry->skb_dev && entry->skb_dev->ifindex == ifindex) ++ return 1; + if (entry->state.in) + if (entry->state.in->ifindex == ifindex) + return 1; diff --git a/queue-5.10/netfilter-nft_fib-fix-stale-stack-leak-via-the-oifname-register.patch b/queue-5.10/netfilter-nft_fib-fix-stale-stack-leak-via-the-oifname-register.patch new file mode 100644 index 0000000000..2ee368bb1a --- /dev/null +++ b/queue-5.10/netfilter-nft_fib-fix-stale-stack-leak-via-the-oifname-register.patch @@ -0,0 +1,86 @@ +From stable+bounces-263424-greg=kroah.com@vger.kernel.org Mon Jun 15 23:33:16 2026 +From: Sasha Levin +Date: Mon, 15 Jun 2026 14:02:45 -0400 +Subject: netfilter: nft_fib: fix stale stack leak via the OIFNAME register +To: stable@vger.kernel.org +Cc: Davide Ornaghi , Florian Westphal , Pablo Neira Ayuso , Sasha Levin +Message-ID: <20260615180245.2316306-1-sashal@kernel.org> + +From: Davide Ornaghi + +[ Upstream commit ab185e0c4fb82dfba6fb86f8271e06f931d9c64c ] + +For NFT_FIB_RESULT_OIFNAME the destination register is declared with +len = IFNAMSIZ (four 32-bit registers), but on the lookup-fail, +RTN_LOCAL and oif-mismatch paths nft_fib{4,6}_eval() only writes one +register via "*dest = 0". The remaining three registers are left as +whatever was on the stack in nft_do_chain()'s struct nft_regs, and a +downstream expression that loads the register span can leak that +uninitialised kernel stack to userspace. + +The NFTA_FIB_F_PRESENT existence check has the same shape: it is only +meaningful for NFT_FIB_RESULT_OIF, yet it was accepted for any result type +while the eval stores a single byte via nft_reg_store8(), leaving the rest +of the declared span stale. + +Fix both: + + - replace the bare "*dest = 0" in the eval with nft_fib_store_result(), + which strscpy_pad()s the whole IFNAMSIZ for OIFNAME (and is already + used on the other early-return path), and + + - restrict NFTA_FIB_F_PRESENT to NFT_FIB_RESULT_OIF and declare its + destination as a single u8, so the marked span matches the one byte + the eval writes. + +Fixes: f6d0cbcf09c5 ("netfilter: nf_tables: add fib expression") +Suggested-by: Florian Westphal +Cc: stable@vger.kernel.org +Signed-off-by: Davide Ornaghi +Signed-off-by: Pablo Neira Ayuso +[ kept the tree's existing `ip6_route_lookup`/`rt6_info` machinery (missing `fib6_lookup` refactor) and changed only `*dest = 0;` to `nft_fib_store_result(dest, priv, NULL)` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/ipv4/netfilter/nft_fib_ipv4.c | 2 +- + net/ipv6/netfilter/nft_fib_ipv6.c | 2 +- + net/netfilter/nft_fib.c | 6 ++++++ + 3 files changed, 8 insertions(+), 2 deletions(-) + +--- a/net/ipv4/netfilter/nft_fib_ipv4.c ++++ b/net/ipv4/netfilter/nft_fib_ipv4.c +@@ -118,7 +118,7 @@ void nft_fib4_eval(const struct nft_expr + fl4.saddr = get_saddr(iph->daddr); + } + +- *dest = 0; ++ nft_fib_store_result(dest, priv, NULL); + + if (fib_lookup(nft_net(pkt), &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE)) + return; +--- a/net/ipv6/netfilter/nft_fib_ipv6.c ++++ b/net/ipv6/netfilter/nft_fib_ipv6.c +@@ -189,7 +189,7 @@ void nft_fib6_eval(const struct nft_expr + } + } + +- *dest = 0; ++ nft_fib_store_result(dest, priv, NULL); + rt = (void *)ip6_route_lookup(nft_net(pkt), &fl6, pkt->skb, + lookup_flags); + if (rt->dst.error) +--- a/net/netfilter/nft_fib.c ++++ b/net/netfilter/nft_fib.c +@@ -105,6 +105,12 @@ int nft_fib_init(const struct nft_ctx *c + return -EINVAL; + } + ++ if (priv->flags & NFTA_FIB_F_PRESENT) { ++ if (priv->result != NFT_FIB_RESULT_OIF) ++ return -EINVAL; ++ len = sizeof(u8); ++ } ++ + err = nft_parse_register_store(ctx, tb[NFTA_FIB_DREG], &priv->dreg, + NULL, NFT_DATA_VALUE, len); + if (err < 0) diff --git a/queue-5.10/octeontx2-af-add-validation-for-lmac-type.patch b/queue-5.10/octeontx2-af-add-validation-for-lmac-type.patch new file mode 100644 index 0000000000..2d595f2f4f --- /dev/null +++ b/queue-5.10/octeontx2-af-add-validation-for-lmac-type.patch @@ -0,0 +1,83 @@ +From stable+bounces-259316-greg=kroah.com@vger.kernel.org Sun May 31 05:54:08 2026 +From: Sasha Levin +Date: Sat, 30 May 2026 20:23:59 -0400 +Subject: octeontx2-af: Add validation for lmac type +To: stable@vger.kernel.org +Cc: Hariprasad Kelam , Sunil Kovvuri Goutham , Sai Krishna , Simon Horman , "David S. Miller" , Sasha Levin +Message-ID: <20260531002401.3659638-1-sashal@kernel.org> + +From: Hariprasad Kelam + +[ Upstream commit cb5edce271764524b88b1a6866b3e626686d9a33 ] + +Upon physical link change, firmware reports to the kernel about the +change along with the details like speed, lmac_type_id, etc. +Kernel derives lmac_type based on lmac_type_id received from firmware. + +In a few scenarios, firmware returns an invalid lmac_type_id, which +is resulting in below kernel panic. This patch adds the missing +validation of the lmac_type_id field. + +Internal error: Oops: 96000005 [#1] PREEMPT SMP +[ 35.321595] Modules linked in: +[ 35.328982] CPU: 0 PID: 31 Comm: kworker/0:1 Not tainted +5.4.210-g2e3169d8e1bc-dirty #17 +[ 35.337014] Hardware name: Marvell CN103XX board (DT) +[ 35.344297] Workqueue: events work_for_cpu_fn +[ 35.352730] pstate: 40400089 (nZcv daIf +PAN -UAO) +[ 35.360267] pc : strncpy+0x10/0x30 +[ 35.366595] lr : cgx_link_change_handler+0x90/0x180 + +Fixes: 61071a871ea6 ("octeontx2-af: Forward CGX link notifications to PFs") +Signed-off-by: Hariprasad Kelam +Signed-off-by: Sunil Kovvuri Goutham +Signed-off-by: Sai Krishna +Reviewed-by: Simon Horman +Signed-off-by: David S. Miller +Stable-dep-of: c0bf0a4f3f1f ("octeontx2-af: CGX: add bounds check to cgx_speed_mbps index") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/ethernet/marvell/octeontx2/af/cgx.c | 15 +++++++++++---- + drivers/net/ethernet/marvell/octeontx2/af/mbox.h | 1 + + 2 files changed, 12 insertions(+), 4 deletions(-) + +--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +@@ -669,15 +669,22 @@ static inline void link_status_user_form + struct cgx_link_user_info *linfo, + struct cgx *cgx, u8 lmac_id) + { +- char *lmac_string; +- + linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat); + linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat); + linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)]; ++ linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat); + linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat); + linfo->lmac_type_id = cgx_get_lmac_type(cgx, lmac_id); +- lmac_string = cgx_lmactype_string[linfo->lmac_type_id]; +- strncpy(linfo->lmac_type, lmac_string, LMACTYPE_STR_LEN - 1); ++ ++ if (linfo->lmac_type_id >= LMAC_MODE_MAX) { ++ dev_err(&cgx->pdev->dev, "Unknown lmac_type_id %d reported by firmware on cgx port%d:%d", ++ linfo->lmac_type_id, cgx->cgx_id, lmac_id); ++ strncpy(linfo->lmac_type, "Unknown", LMACTYPE_STR_LEN - 1); ++ return; ++ } ++ ++ strncpy(linfo->lmac_type, cgx_lmactype_string[linfo->lmac_type_id], ++ LMACTYPE_STR_LEN - 1); + } + + /* Hardware event handlers */ +--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h ++++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +@@ -369,6 +369,7 @@ struct cgx_link_user_info { + uint64_t full_duplex:1; + uint64_t lmac_type_id:4; + uint64_t speed:20; /* speed in Mbps */ ++ uint64_t an:1; /* AN supported or not */ + uint64_t fec:2; /* FEC type if enabled else 0 */ + #define LMACTYPE_STR_LEN 16 + char lmac_type[LMACTYPE_STR_LEN]; diff --git a/queue-5.10/octeontx2-af-cgx-add-bounds-check-to-cgx_speed_mbps-index.patch b/queue-5.10/octeontx2-af-cgx-add-bounds-check-to-cgx_speed_mbps-index.patch new file mode 100644 index 0000000000..91fa0f3733 --- /dev/null +++ b/queue-5.10/octeontx2-af-cgx-add-bounds-check-to-cgx_speed_mbps-index.patch @@ -0,0 +1,57 @@ +From stable+bounces-259318-greg=kroah.com@vger.kernel.org Sun May 31 05:54:09 2026 +From: Sasha Levin +Date: Sat, 30 May 2026 20:24:01 -0400 +Subject: octeontx2-af: CGX: add bounds check to cgx_speed_mbps index +To: stable@vger.kernel.org +Cc: Sam Daly , Sunil Goutham , Linu Cherian , Geetha sowjanya , hariprasad , Subbaraya Sundeep , Andrew Lunn , stable , Greg Kroah-Hartman , Jakub Kicinski , Sasha Levin +Message-ID: <20260531002401.3659638-3-sashal@kernel.org> + +From: Sam Daly + +[ Upstream commit c0bf0a4f3f1f5f57aa83e1400ba4f56f0abfd542 ] + +cgx_speed_mbps has 13 elements but RESP_LINKSTAT_SPEED can yield values +0-15. If it returns a value >= 13, this causes an out-of-bounds array +access. Add a bounds check and default to speed 0 if the index is out of +range. + +Fixes: 61071a871ea6 ("octeontx2-af: Forward CGX link notifications to PFs") +Cc: Sunil Goutham +Cc: Linu Cherian +Cc: Geetha sowjanya +Cc: hariprasad +Cc: Subbaraya Sundeep +Cc: Andrew Lunn +Cc: stable +Signed-off-by: Sam Daly +Signed-off-by: Greg Kroah-Hartman +Link: https://patch.msgid.link/2026051352-refined-demise-e88d@gregkh +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/ethernet/marvell/octeontx2/af/cgx.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +@@ -669,13 +669,18 @@ static inline void link_status_user_form + struct cgx_link_user_info *linfo, + struct cgx *cgx, u8 lmac_id) + { ++ unsigned int speed; ++ + linfo->link_up = FIELD_GET(RESP_LINKSTAT_UP, lstat); + linfo->full_duplex = FIELD_GET(RESP_LINKSTAT_FDUPLEX, lstat); +- linfo->speed = cgx_speed_mbps[FIELD_GET(RESP_LINKSTAT_SPEED, lstat)]; + linfo->an = FIELD_GET(RESP_LINKSTAT_AN, lstat); + linfo->fec = FIELD_GET(RESP_LINKSTAT_FEC, lstat); + linfo->lmac_type_id = cgx_get_lmac_type(cgx, lmac_id); + ++ speed = FIELD_GET(RESP_LINKSTAT_SPEED, lstat); ++ linfo->speed = speed < ARRAY_SIZE(cgx_speed_mbps) ? ++ cgx_speed_mbps[speed] : 0; ++ + if (linfo->lmac_type_id >= LMAC_MODE_MAX) { + dev_err(&cgx->pdev->dev, "Unknown lmac_type_id %d reported by firmware on cgx port%d:%d", + linfo->lmac_type_id, cgx->cgx_id, lmac_id); diff --git a/queue-5.10/octeontx2-af-replace-deprecated-strncpy-with-strscpy.patch b/queue-5.10/octeontx2-af-replace-deprecated-strncpy-with-strscpy.patch new file mode 100644 index 0000000000..ca2fe2a18a --- /dev/null +++ b/queue-5.10/octeontx2-af-replace-deprecated-strncpy-with-strscpy.patch @@ -0,0 +1,59 @@ +From stable+bounces-259317-greg=kroah.com@vger.kernel.org Sun May 31 05:54:08 2026 +From: Sasha Levin +Date: Sat, 30 May 2026 20:24:00 -0400 +Subject: octeontx2-af: replace deprecated strncpy with strscpy +To: stable@vger.kernel.org +Cc: Justin Stitt , Kees Cook , Jakub Kicinski , Sasha Levin +Message-ID: <20260531002401.3659638-2-sashal@kernel.org> + +From: Justin Stitt + +[ Upstream commit 473f8f2d1bfe1103f20140fdc80cad406b4d68c0 ] + +`strncpy` is deprecated for use on NUL-terminated destination strings +[1] and as such we should prefer more robust and less ambiguous string +interfaces. + +We can see that linfo->lmac_type is expected to be NUL-terminated based +on the `... - 1`'s present in the current code. Presumably making room +for a NUL-byte at the end of the buffer. + +Considering the above, a suitable replacement is `strscpy` [2] due to +the fact that it guarantees NUL-termination on the destination buffer +without unnecessarily NUL-padding. + +Let's also prefer the more idiomatic strscpy usage of (dest, src, +sizeof(dest)) rather than (dest, src, SOME_LEN). + +Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strncpy-on-nul-terminated-strings [1] +Link: https://manpages.debian.org/testing/linux-manual-4.8/strscpy.9.en.html [2] +Link: https://github.com/KSPP/linux/issues/90 +Signed-off-by: Justin Stitt +Reviewed-by: Kees Cook +Link: https://lore.kernel.org/r/20231010-strncpy-drivers-net-ethernet-marvell-octeontx2-af-cgx-c-v1-1-a443e18f9de8@google.com +Signed-off-by: Jakub Kicinski +Stable-dep-of: c0bf0a4f3f1f ("octeontx2-af: CGX: add bounds check to cgx_speed_mbps index") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/ethernet/marvell/octeontx2/af/cgx.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c ++++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +@@ -679,12 +679,12 @@ static inline void link_status_user_form + if (linfo->lmac_type_id >= LMAC_MODE_MAX) { + dev_err(&cgx->pdev->dev, "Unknown lmac_type_id %d reported by firmware on cgx port%d:%d", + linfo->lmac_type_id, cgx->cgx_id, lmac_id); +- strncpy(linfo->lmac_type, "Unknown", LMACTYPE_STR_LEN - 1); ++ strscpy(linfo->lmac_type, "Unknown", sizeof(linfo->lmac_type)); + return; + } + +- strncpy(linfo->lmac_type, cgx_lmactype_string[linfo->lmac_type_id], +- LMACTYPE_STR_LEN - 1); ++ strscpy(linfo->lmac_type, cgx_lmactype_string[linfo->lmac_type_id], ++ sizeof(linfo->lmac_type)); + } + + /* Hardware event handlers */ diff --git a/queue-5.10/octeontx2-pf-avoid-double-free-of-pool-stack-on-aq-init-failure.patch b/queue-5.10/octeontx2-pf-avoid-double-free-of-pool-stack-on-aq-init-failure.patch new file mode 100644 index 0000000000..e0529546ab --- /dev/null +++ b/queue-5.10/octeontx2-pf-avoid-double-free-of-pool-stack-on-aq-init-failure.patch @@ -0,0 +1,62 @@ +From stable+bounces-259300-greg=kroah.com@vger.kernel.org Sun May 31 02:03:24 2026 +From: Sasha Levin +Date: Sat, 30 May 2026 16:33:16 -0400 +Subject: octeontx2-pf: avoid double free of pool->stack on AQ init failure +To: stable@vger.kernel.org +Cc: Dawei Feng , Zilin Guan , Simon Horman , Jakub Kicinski , Sasha Levin +Message-ID: <20260530203316.3338022-1-sashal@kernel.org> + +From: Dawei Feng + +[ Upstream commit 9b244c242bec48b37e82b89787afd6a4c43457e1 ] + +otx2_pool_aq_init() frees pool->stack when mailbox sync or retry +allocation fails, but leaves the pointer unchanged. Later, +otx2_sq_aura_pool_init() unwinds the partial setup through +otx2_aura_pool_free(), which frees pool->stack again. The CN20K-specific +cn20k_pool_aq_init() implementation has the same bug in +its corresponding error path. + +Set pool->stack to NULL immediately after the local free so the shared +cleanup path does not free the same stack again while cleaning up +partially initialized pool state. + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still present in +v7.1-rc3. + +Runtime validation was not performed because reproducing this path +requires OcteonTX2/CN20K hardware. + +Fixes: caa2da34fd25 ("octeontx2-pf: Initialize and config queues") +Fixes: d322fbd17203 ("octeontx2-pf: Initialize cn20k specific aura and pool contexts") +Cc: stable@vger.kernel.org +Signed-off-by: Zilin Guan +Signed-off-by: Dawei Feng +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260515151826.1005397-1-dawei.feng@seu.edu.cn +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c ++++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +@@ -1194,11 +1194,13 @@ static int otx2_pool_init(struct otx2_ni + err = otx2_sync_mbox_msg(&pfvf->mbox); + if (err) { + qmem_free(pfvf->dev, pool->stack); ++ pool->stack = NULL; + return err; + } + aq = otx2_mbox_alloc_msg_npa_aq_enq(&pfvf->mbox); + if (!aq) { + qmem_free(pfvf->dev, pool->stack); ++ pool->stack = NULL; + return -ENOMEM; + } + } diff --git a/queue-5.10/phy-tegra-xusb-disable-trk-clk-when-not-in-use.patch b/queue-5.10/phy-tegra-xusb-disable-trk-clk-when-not-in-use.patch new file mode 100644 index 0000000000..7834607cb6 --- /dev/null +++ b/queue-5.10/phy-tegra-xusb-disable-trk-clk-when-not-in-use.patch @@ -0,0 +1,51 @@ +From sashal@kernel.org Mon Jun 1 16:38:12 2026 +From: Sasha Levin +Date: Mon, 1 Jun 2026 07:08:08 -0400 +Subject: phy: tegra: xusb: Disable trk clk when not in use +To: stable@vger.kernel.org +Cc: Wayne Chang , Jon Hunter , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260601110809.450763-1-sashal@kernel.org> + +From: Wayne Chang + +[ Upstream commit 71d9e899584e11bbd7eaf9934a619c69a15060d8 ] + +Pad tracking is a one-time calibration for Tegra186 and Tegra194. +Clk should be disabled after calibration. + +Disable clk after calibration. +While at it add 100us delay for HW recording the calibration value. + +Signed-off-by: Wayne Chang +Signed-off-by: Jon Hunter +Link: https://lore.kernel.org/r/20230111110450.24617-5-jonathanh@nvidia.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: da110228b54f ("phy: tegra: xusb: Fix per-pad high-speed termination calibration") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/phy/tegra/xusb-tegra186.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/phy/tegra/xusb-tegra186.c ++++ b/drivers/phy/tegra/xusb-tegra186.c +@@ -225,6 +225,10 @@ static void tegra186_utmi_bias_pad_power + value &= ~USB2_PD_TRK; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + ++ udelay(100); ++ ++ clk_disable_unprepare(priv->usb2_trk_clk); ++ + mutex_unlock(&padctl->lock); + } + +@@ -249,8 +253,6 @@ static void tegra186_utmi_bias_pad_power + value |= USB2_PD_TRK; + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1); + +- clk_disable_unprepare(priv->usb2_trk_clk); +- + mutex_unlock(&padctl->lock); + } + diff --git a/queue-5.10/phy-tegra-xusb-fix-per-pad-high-speed-termination-calibration.patch b/queue-5.10/phy-tegra-xusb-fix-per-pad-high-speed-termination-calibration.patch new file mode 100644 index 0000000000..2f83db90a0 --- /dev/null +++ b/queue-5.10/phy-tegra-xusb-fix-per-pad-high-speed-termination-calibration.patch @@ -0,0 +1,144 @@ +From stable+bounces-259532-greg=kroah.com@vger.kernel.org Mon Jun 1 16:40:27 2026 +From: Sasha Levin +Date: Mon, 1 Jun 2026 07:08:09 -0400 +Subject: phy: tegra: xusb: Fix per-pad high-speed termination calibration +To: stable@vger.kernel.org +Cc: Wayne Chang , Wei-Cheng Chen , Jon Hunter , Vinod Koul , Sasha Levin +Message-ID: <20260601110809.450763-2-sashal@kernel.org> + +From: Wayne Chang + +[ Upstream commit da110228b54f2e2143d97ea7151e0dc22e539d67 ] + +The existing code reads a single hs_term_range_adj value from bit field +[10:7] of FUSE_SKU_CALIB_0 and applies it to all USB2 pads uniformly. +However, on SoCs that support per-pad termination, each pad has its own +hs_term_range_adj field: pad 0 in FUSE_SKU_CALIB_0[10:7], and pads 1-3 +in FUSE_USB_CALIB_EXT_0 at bit offsets [8:5], [12:9], and [16:13] +respectively. + +Fix the calibration by reading per-pad values from the appropriate fuse +registers. For SoCs that do not support per-pad termination, replicate +pad 0's value to all pads to maintain existing behavior. + +Add a has_per_pad_term flag to the SoC data to indicate whether per-pad +termination values are available in FUSE_USB_CALIB_EXT_0. + +Fixes: 1ef535c6ba8e ("phy: tegra: xusb: Add Tegra194 support") +Cc: stable@vger.kernel.org +Signed-off-by: Wayne Chang +Signed-off-by: Wei-Cheng Chen +Reviewed-by: Jon Hunter +Tested-by: Jon Hunter +Link: https://patch.msgid.link/20260504033305.2283145-1-weichengc@nvidia.com +Signed-off-by: Vinod Koul +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/phy/tegra/xusb-tegra186.c | 32 +++++++++++++++++++++++++------- + drivers/phy/tegra/xusb.h | 1 + + 2 files changed, 26 insertions(+), 7 deletions(-) + +--- a/drivers/phy/tegra/xusb-tegra186.c ++++ b/drivers/phy/tegra/xusb-tegra186.c +@@ -20,8 +20,8 @@ + /* FUSE USB_CALIB registers */ + #define HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? (11 + (x - 1) * 6) : 0) + #define HS_CURR_LEVEL_PAD_MASK 0x3f +-#define HS_TERM_RANGE_ADJ_SHIFT 7 +-#define HS_TERM_RANGE_ADJ_MASK 0xf ++#define HS_TERM_RANGE_ADJ_PADX_SHIFT(x) ((x) ? (5 + (x - 1) * 4) : 7) ++#define HS_TERM_RANGE_ADJ_PAD_MASK 0xf + #define HS_SQUELCH_SHIFT 29 + #define HS_SQUELCH_MASK 0x7 + +@@ -127,7 +127,7 @@ + struct tegra_xusb_fuse_calibration { + u32 *hs_curr_level; + u32 hs_squelch; +- u32 hs_term_range_adj; ++ u32 *hs_term_range_adj; + u32 rpd_ctrl; + }; + +@@ -477,7 +477,7 @@ static int tegra186_utmi_phy_power_on(st + + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); + value &= ~TERM_RANGE_ADJ(~0); +- value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj); ++ value |= TERM_RANGE_ADJ(priv->calib.hs_term_range_adj[index]); + value &= ~RPD_CTRL(~0); + value |= RPD_CTRL(priv->calib.rpd_ctrl); + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index)); +@@ -895,17 +895,23 @@ static const char * const tegra186_usb3_ + static int + tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl) + { ++ const struct tegra_xusb_padctl_soc *soc = padctl->base.soc; + struct device *dev = padctl->base.dev; + unsigned int i, count; + u32 value, *level; ++ u32 *hs_term_range_adj; + int err; + +- count = padctl->base.soc->ports.usb2.count; ++ count = soc->ports.usb2.count; + + level = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL); + if (!level) + return -ENOMEM; + ++ hs_term_range_adj = devm_kcalloc(dev, count, sizeof(u32), GFP_KERNEL); ++ if (!hs_term_range_adj) ++ return -ENOMEM; ++ + err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value); + if (err) { + if (err != -EPROBE_DEFER) +@@ -924,8 +930,8 @@ tegra186_xusb_read_fuse_calibration(stru + + padctl->calib.hs_squelch = (value >> HS_SQUELCH_SHIFT) & + HS_SQUELCH_MASK; +- padctl->calib.hs_term_range_adj = (value >> HS_TERM_RANGE_ADJ_SHIFT) & +- HS_TERM_RANGE_ADJ_MASK; ++ hs_term_range_adj[0] = (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(0)) & ++ HS_TERM_RANGE_ADJ_PAD_MASK; + + err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value); + if (err) { +@@ -937,6 +943,17 @@ tegra186_xusb_read_fuse_calibration(stru + + padctl->calib.rpd_ctrl = (value >> RPD_CTRL_SHIFT) & RPD_CTRL_MASK; + ++ for (i = 1; i < count; i++) { ++ if (soc->has_per_pad_term) ++ hs_term_range_adj[i] = ++ (value >> HS_TERM_RANGE_ADJ_PADX_SHIFT(i)) & ++ HS_TERM_RANGE_ADJ_PAD_MASK; ++ else ++ hs_term_range_adj[i] = hs_term_range_adj[0]; ++ } ++ ++ padctl->calib.hs_term_range_adj = hs_term_range_adj; ++ + return 0; + } + +@@ -1095,6 +1112,7 @@ const struct tegra_xusb_padctl_soc tegra + .supply_names = tegra194_xusb_padctl_supply_names, + .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names), + .supports_gen2 = true, ++ .has_per_pad_term = true, + }; + EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc); + #endif +--- a/drivers/phy/tegra/xusb.h ++++ b/drivers/phy/tegra/xusb.h +@@ -415,6 +415,7 @@ struct tegra_xusb_padctl_soc { + unsigned int num_supplies; + bool supports_gen2; + bool need_fake_usb3_port; ++ bool has_per_pad_term; + }; + + struct tegra_xusb_padctl { diff --git a/queue-5.10/pmdomain-core-fix-detach-procedure-for-virtual-devices-in-genpd.patch b/queue-5.10/pmdomain-core-fix-detach-procedure-for-virtual-devices-in-genpd.patch new file mode 100644 index 0000000000..ee58634303 --- /dev/null +++ b/queue-5.10/pmdomain-core-fix-detach-procedure-for-virtual-devices-in-genpd.patch @@ -0,0 +1,73 @@ +From stable+bounces-248924-greg=kroah.com@vger.kernel.org Sat May 16 03:20:48 2026 +From: Sasha Levin +Date: Fri, 15 May 2026 17:35:39 -0400 +Subject: pmdomain: core: Fix detach procedure for virtual devices in genpd +To: stable@vger.kernel.org +Cc: Ulf Hansson , Geert Uytterhoeven , Geert Uytterhoeven , Sasha Levin +Message-ID: <20260515213539.3512446-1-sashal@kernel.org> + +From: Ulf Hansson + +[ Upstream commit 26735dfdd8930d9ef1fa92e590a9bf77726efdf6 ] + +If a device is attached to a PM domain through genpd_dev_pm_attach_by_id(), +genpd calls pm_runtime_enable() for the corresponding virtual device that +it registers. While this avoids boilerplate code in drivers, there is no +corresponding call to pm_runtime_disable() in genpd_dev_pm_detach(). + +This means these virtual devices are typically detached from its genpd, +while runtime PM remains enabled for them, which is not how things are +designed to work. In worst cases it may lead to critical errors, like a +NULL pointer dereference bug in genpd_runtime_suspend(), which was recently +reported. For another case, we may end up keeping an unnecessary vote for a +performance state for the device. + +To fix these problems, let's add this missing call to pm_runtime_disable() +in genpd_dev_pm_detach(). + +Reported-by: Geert Uytterhoeven +Closes: https://lore.kernel.org/all/CAMuHMdWapT40hV3c+CSBqFOW05aWcV1a6v_NiJYgoYi0i9_PDQ@mail.gmail.com/ +Fixes: 3c095f32a92b ("PM / Domains: Add support for multi PM domains per device to genpd") +Cc: stable@vger.kernel.org +Tested-by: Geert Uytterhoeven +Signed-off-by: Ulf Hansson +[ dropped upstream context block referencing nonexistent `default_pstate` field ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/base/power/domain.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/base/power/domain.c ++++ b/drivers/base/power/domain.c +@@ -2525,6 +2525,7 @@ static struct bus_type genpd_bus_type = + static void genpd_dev_pm_detach(struct device *dev, bool power_off) + { + struct generic_pm_domain *pd; ++ bool is_virt_dev; + unsigned int i; + int ret = 0; + +@@ -2534,6 +2535,13 @@ static void genpd_dev_pm_detach(struct d + + dev_dbg(dev, "removing from PM domain %s\n", pd->name); + ++ /* Check if the device was created by genpd at attach. */ ++ is_virt_dev = dev->bus == &genpd_bus_type; ++ ++ /* Disable runtime PM if we enabled it at attach. */ ++ if (is_virt_dev) ++ pm_runtime_disable(dev); ++ + for (i = 1; i < GENPD_RETRY_MAX_MS; i <<= 1) { + ret = genpd_remove_device(pd, dev); + if (ret != -EAGAIN) +@@ -2553,7 +2561,7 @@ static void genpd_dev_pm_detach(struct d + genpd_queue_power_off_work(pd); + + /* Unregister the device if it was created by genpd. */ +- if (dev->bus == &genpd_bus_type) ++ if (is_virt_dev) + device_unregister(dev); + } + diff --git a/queue-5.10/printk-add-print_hex_dump_devel.patch b/queue-5.10/printk-add-print_hex_dump_devel.patch new file mode 100644 index 0000000000..82c68c6ced --- /dev/null +++ b/queue-5.10/printk-add-print_hex_dump_devel.patch @@ -0,0 +1,49 @@ +From stable+bounces-245036-greg=kroah.com@vger.kernel.org Sun May 10 21:13:50 2026 +From: Sasha Levin +Date: Sun, 10 May 2026 11:43:37 -0400 +Subject: printk: add print_hex_dump_devel() +To: stable@vger.kernel.org +Cc: Thorsten Blum , Herbert Xu , John Ogness , Sasha Levin +Message-ID: <20260510154339.144690-1-sashal@kernel.org> + +From: Thorsten Blum + +[ Upstream commit d134feeb5df33fbf77f482f52a366a44642dba09 ] + +Add print_hex_dump_devel() as the hex dump equivalent of pr_devel(), +which emits output only when DEBUG is enabled, but keeps call sites +compiled otherwise. + +Suggested-by: Herbert Xu +Signed-off-by: Thorsten Blum +Reviewed-by: John Ogness +Signed-off-by: Herbert Xu +Stable-dep-of: 177730a273b1 ("crypto: caam - guard HMAC key hex dumps in hash_digest_key") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/printk.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/include/linux/printk.h ++++ b/include/linux/printk.h +@@ -608,6 +608,19 @@ static inline void print_hex_dump_debug( + } + #endif + ++#if defined(DEBUG) ++#define print_hex_dump_devel(prefix_str, prefix_type, rowsize, \ ++ groupsize, buf, len, ascii) \ ++ print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \ ++ groupsize, buf, len, ascii) ++#else ++static inline void print_hex_dump_devel(const char *prefix_str, int prefix_type, ++ int rowsize, int groupsize, ++ const void *buf, size_t len, bool ascii) ++{ ++} ++#endif ++ + /** + * print_hex_dump_bytes - shorthand form of print_hex_dump_debug() with default + * params diff --git a/queue-5.10/qed-fix-double-free-in-qed_cxt_tables_alloc.patch b/queue-5.10/qed-fix-double-free-in-qed_cxt_tables_alloc.patch new file mode 100644 index 0000000000..ed40523687 --- /dev/null +++ b/queue-5.10/qed-fix-double-free-in-qed_cxt_tables_alloc.patch @@ -0,0 +1,58 @@ +From stable+bounces-256819-greg=kroah.com@vger.kernel.org Sat May 30 05:07:32 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 19:33:25 -0400 +Subject: qed: fix double free in qed_cxt_tables_alloc() +To: stable@vger.kernel.org +Cc: Dawei Feng , Zilin Guan , Jakub Kicinski , Sasha Levin +Message-ID: <20260529233325.1901920-2-sashal@kernel.org> + +From: Dawei Feng + +[ Upstream commit 2bccfb8476ca5f3548afbd623dc7a6980d4e77de ] + +If one of the later PF or VF CID bitmap allocations fails, +qed_cid_map_alloc() jumps to cid_map_fail and frees the previously +allocated CID bitmaps before returning an error. qed_cxt_tables_alloc() +then calls qed_cxt_mngr_free(), which invokes qed_cid_map_free() +again. + +Fix this by setting each CID bitmap pointer to NULL after bitmap_free() +to avoid double free. + +The bug was first flagged by an experimental analysis tool we are +developing for kernel memory-management bugs while analyzing +v6.13-rc1. The tool is still under development and is not yet publicly +available. Manual inspection confirms that the bug is still +present in v7.1-rc3. + +Runtime reproduction was not attempted because exercising the failing +allocation path requires device-specific setup. + +Fixes: fe56b9e6a8d9 ("qed: Add module with basic common support") +Cc: stable@vger.kernel.org +Signed-off-by: Zilin Guan +Signed-off-by: Dawei Feng +Link: https://patch.msgid.link/20260520070323.2762379-1-dawei.feng@seu.edu.cn +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/ethernet/qlogic/qed/qed_cxt.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c ++++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c +@@ -1038,11 +1038,13 @@ static void qed_cid_map_free(struct qed_ + + for (type = 0; type < MAX_CONN_TYPES; type++) { + bitmap_free(p_mngr->acquired[type].cid_map); ++ p_mngr->acquired[type].cid_map = NULL; + p_mngr->acquired[type].max_count = 0; + p_mngr->acquired[type].start_cid = 0; + + for (vf = 0; vf < MAX_NUM_VFS; vf++) { + bitmap_free(p_mngr->acquired_vf[type][vf].cid_map); ++ p_mngr->acquired_vf[type][vf].cid_map = NULL; + p_mngr->acquired_vf[type][vf].max_count = 0; + p_mngr->acquired_vf[type][vf].start_cid = 0; + } diff --git a/queue-5.10/qed-use-the-bitmap-api-to-simplify-some-functions.patch b/queue-5.10/qed-use-the-bitmap-api-to-simplify-some-functions.patch new file mode 100644 index 0000000000..8053d2a7d0 --- /dev/null +++ b/queue-5.10/qed-use-the-bitmap-api-to-simplify-some-functions.patch @@ -0,0 +1,99 @@ +From stable+bounces-256818-greg=kroah.com@vger.kernel.org Sat May 30 05:07:21 2026 +From: Sasha Levin +Date: Fri, 29 May 2026 19:33:24 -0400 +Subject: qed: Use the bitmap API to simplify some functions +To: stable@vger.kernel.org +Cc: Christophe JAILLET , "David S. Miller" , Sasha Levin +Message-ID: <20260529233325.1901920-1-sashal@kernel.org> + +From: Christophe JAILLET + +[ Upstream commit 5e6c7ccd3ea4b25dd6b4b0363859913f315deacb ] + +'cid_map' is a bitmap. So use 'bitmap_zalloc()' to simplify code, +improve the semantic and avoid some open-coded arithmetic in allocator +arguments. + +Also change the corresponding 'kfree()' into 'bitmap_free()' to keep +consistency. + +Also change some 'memset()' into 'bitmap_zero()' to keep consistency. This +is also much less verbose. + +Signed-off-by: Christophe JAILLET +Signed-off-by: David S. Miller +Stable-dep-of: 2bccfb8476ca ("qed: fix double free in qed_cxt_tables_alloc()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/ethernet/qlogic/qed/qed_cxt.c | 24 +++++------------------- + 1 file changed, 5 insertions(+), 19 deletions(-) + +--- a/drivers/net/ethernet/qlogic/qed/qed_cxt.c ++++ b/drivers/net/ethernet/qlogic/qed/qed_cxt.c +@@ -1037,12 +1037,12 @@ static void qed_cid_map_free(struct qed_ + u32 type, vf; + + for (type = 0; type < MAX_CONN_TYPES; type++) { +- kfree(p_mngr->acquired[type].cid_map); ++ bitmap_free(p_mngr->acquired[type].cid_map); + p_mngr->acquired[type].max_count = 0; + p_mngr->acquired[type].start_cid = 0; + + for (vf = 0; vf < MAX_NUM_VFS; vf++) { +- kfree(p_mngr->acquired_vf[type][vf].cid_map); ++ bitmap_free(p_mngr->acquired_vf[type][vf].cid_map); + p_mngr->acquired_vf[type][vf].max_count = 0; + p_mngr->acquired_vf[type][vf].start_cid = 0; + } +@@ -1055,15 +1055,10 @@ qed_cid_map_alloc_single(struct qed_hwfn + u32 cid_start, + u32 cid_count, struct qed_cid_acquired_map *p_map) + { +- u32 size; +- + if (!cid_count) + return 0; + +- size = DIV_ROUND_UP(cid_count, +- sizeof(unsigned long) * BITS_PER_BYTE) * +- sizeof(unsigned long); +- p_map->cid_map = kzalloc(size, GFP_KERNEL); ++ p_map->cid_map = bitmap_zalloc(cid_count, GFP_KERNEL); + if (!p_map->cid_map) + return -ENOMEM; + +@@ -1217,7 +1212,6 @@ void qed_cxt_mngr_setup(struct qed_hwfn + struct qed_cid_acquired_map *p_map; + struct qed_conn_type_cfg *p_cfg; + int type; +- u32 len; + + /* Reset acquired cids */ + for (type = 0; type < MAX_CONN_TYPES; type++) { +@@ -1226,11 +1220,7 @@ void qed_cxt_mngr_setup(struct qed_hwfn + p_cfg = &p_mngr->conn_cfg[type]; + if (p_cfg->cid_count) { + p_map = &p_mngr->acquired[type]; +- len = DIV_ROUND_UP(p_map->max_count, +- sizeof(unsigned long) * +- BITS_PER_BYTE) * +- sizeof(unsigned long); +- memset(p_map->cid_map, 0, len); ++ bitmap_zero(p_map->cid_map, p_map->max_count); + } + + if (!p_cfg->cids_per_vf) +@@ -1238,11 +1228,7 @@ void qed_cxt_mngr_setup(struct qed_hwfn + + for (vf = 0; vf < MAX_NUM_VFS; vf++) { + p_map = &p_mngr->acquired_vf[type][vf]; +- len = DIV_ROUND_UP(p_map->max_count, +- sizeof(unsigned long) * +- BITS_PER_BYTE) * +- sizeof(unsigned long); +- memset(p_map->cid_map, 0, len); ++ bitmap_zero(p_map->cid_map, p_map->max_count); + } + } + } diff --git a/queue-5.10/rdma-move-dma-block-iterator-logic-into-dedicated-files.patch b/queue-5.10/rdma-move-dma-block-iterator-logic-into-dedicated-files.patch new file mode 100644 index 0000000000..a2b73ea0ad --- /dev/null +++ b/queue-5.10/rdma-move-dma-block-iterator-logic-into-dedicated-files.patch @@ -0,0 +1,451 @@ +From stable+bounces-263530-greg=kroah.com@vger.kernel.org Tue Jun 16 08:47:46 2026 +From: Sasha Levin +Date: Mon, 15 Jun 2026 23:17:36 -0400 +Subject: RDMA: Move DMA block iterator logic into dedicated files +To: stable@vger.kernel.org +Cc: Leon Romanovsky , Sasha Levin +Message-ID: <20260616031737.2781524-2-sashal@kernel.org> + +From: Leon Romanovsky + +[ Upstream commit 6094ea64c69520ed1e770e7c79c43412de202bfa ] + +The DMA iterator logic was mixed into verbs and umem-specific code, +forcing all users to include rdma/ib_umem.h. Move the block iterator +logic into iter.c and rdma/iter.h so that rdma/ib_umem.h and +rdma/ib_verbs.h can be separated in a follow-up patch. + +Link: https://patch.msgid.link/20260213-refactor-umem-v1-1-f3be85847922@nvidia.com +Signed-off-by: Leon Romanovsky +Stable-dep-of: 15fe76e23615 ("RDMA/umem: Fix truncation for block sizes >= 4G") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/infiniband/core/Makefile | 2 + drivers/infiniband/core/iter.c | 43 ++++++++++++++ + drivers/infiniband/core/verbs.c | 37 ------------ + drivers/infiniband/hw/bnxt_re/qplib_res.c | 2 + drivers/infiniband/hw/cxgb4/mem.c | 2 + drivers/infiniband/hw/efa/efa_verbs.c | 2 + drivers/infiniband/hw/hns/hns_roce_alloc.c | 2 + drivers/infiniband/hw/i40iw/i40iw_verbs.c | 1 + drivers/infiniband/hw/mlx4/mr.c | 1 + drivers/infiniband/hw/mlx5/mem.c | 1 + drivers/infiniband/hw/mthca/mthca_provider.c | 2 + drivers/infiniband/hw/ocrdma/ocrdma_verbs.c | 2 + drivers/infiniband/hw/qedr/verbs.c | 2 + drivers/infiniband/hw/vmw_pvrdma/pvrdma.h | 2 + include/rdma/ib_umem.h | 24 -------- + include/rdma/ib_verbs.h | 47 --------------- + include/rdma/iter.h | 80 +++++++++++++++++++++++++++ + 17 files changed, 135 insertions(+), 117 deletions(-) + create mode 100644 drivers/infiniband/core/iter.c + create mode 100644 include/rdma/iter.h + +--- a/drivers/infiniband/core/Makefile ++++ b/drivers/infiniband/core/Makefile +@@ -12,7 +12,7 @@ ib_core-y := packer.o ud_header.o verb + roce_gid_mgmt.o mr_pool.o addr.o sa_query.o \ + multicast.o mad.o smi.o agent.o mad_rmpp.o \ + nldev.o restrack.o counters.o ib_core_uverbs.o \ +- trace.o lag.o ++ trace.o lag.o iter.o + + ib_core-$(CONFIG_SECURITY_INFINIBAND) += security.o + ib_core-$(CONFIG_CGROUP_RDMA) += cgroup.o +--- /dev/null ++++ b/drivers/infiniband/core/iter.c +@@ -0,0 +1,43 @@ ++// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB ++/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. */ ++ ++#include ++#include ++ ++void __rdma_block_iter_start(struct ib_block_iter *biter, ++ struct scatterlist *sglist, unsigned int nents, ++ unsigned long pgsz) ++{ ++ memset(biter, 0, sizeof(struct ib_block_iter)); ++ biter->__sg = sglist; ++ biter->__sg_nents = nents; ++ ++ /* Driver provides best block size to use */ ++ biter->__pg_bit = __fls(pgsz); ++} ++EXPORT_SYMBOL(__rdma_block_iter_start); ++ ++bool __rdma_block_iter_next(struct ib_block_iter *biter) ++{ ++ unsigned int block_offset; ++ unsigned int delta; ++ ++ if (!biter->__sg_nents || !biter->__sg) ++ return false; ++ ++ biter->__dma_addr = sg_dma_address(biter->__sg) + biter->__sg_advance; ++ block_offset = biter->__dma_addr & (BIT_ULL(biter->__pg_bit) - 1); ++ delta = BIT_ULL(biter->__pg_bit) - block_offset; ++ ++ while (biter->__sg_nents && biter->__sg && ++ sg_dma_len(biter->__sg) - biter->__sg_advance <= delta) { ++ delta -= sg_dma_len(biter->__sg) - biter->__sg_advance; ++ biter->__sg_advance = 0; ++ biter->__sg = sg_next(biter->__sg); ++ biter->__sg_nents--; ++ } ++ biter->__sg_advance += delta; ++ ++ return true; ++} ++EXPORT_SYMBOL(__rdma_block_iter_next); +--- a/drivers/infiniband/core/verbs.c ++++ b/drivers/infiniband/core/verbs.c +@@ -2896,40 +2896,3 @@ int rdma_init_netdev(struct ib_device *d + netdev, params.param); + } + EXPORT_SYMBOL(rdma_init_netdev); +- +-void __rdma_block_iter_start(struct ib_block_iter *biter, +- struct scatterlist *sglist, unsigned int nents, +- unsigned long pgsz) +-{ +- memset(biter, 0, sizeof(struct ib_block_iter)); +- biter->__sg = sglist; +- biter->__sg_nents = nents; +- +- /* Driver provides best block size to use */ +- biter->__pg_bit = __fls(pgsz); +-} +-EXPORT_SYMBOL(__rdma_block_iter_start); +- +-bool __rdma_block_iter_next(struct ib_block_iter *biter) +-{ +- unsigned int block_offset; +- unsigned int sg_delta; +- +- if (!biter->__sg_nents || !biter->__sg) +- return false; +- +- biter->__dma_addr = sg_dma_address(biter->__sg) + biter->__sg_advance; +- block_offset = biter->__dma_addr & (BIT_ULL(biter->__pg_bit) - 1); +- sg_delta = BIT_ULL(biter->__pg_bit) - block_offset; +- +- if (sg_dma_len(biter->__sg) - biter->__sg_advance > sg_delta) { +- biter->__sg_advance += sg_delta; +- } else { +- biter->__sg_advance = 0; +- biter->__sg = sg_next(biter->__sg); +- biter->__sg_nents--; +- } +- +- return true; +-} +-EXPORT_SYMBOL(__rdma_block_iter_next); +--- a/drivers/infiniband/hw/bnxt_re/qplib_res.c ++++ b/drivers/infiniband/hw/bnxt_re/qplib_res.c +@@ -46,7 +46,7 @@ + #include + #include + #include +-#include ++#include + + #include "roce_hsi.h" + #include "qplib_res.h" +--- a/drivers/infiniband/hw/cxgb4/mem.c ++++ b/drivers/infiniband/hw/cxgb4/mem.c +@@ -32,9 +32,9 @@ + + #include + #include +-#include + #include + #include ++#include + + #include "iw_cxgb4.h" + +--- a/drivers/infiniband/hw/efa/efa_verbs.c ++++ b/drivers/infiniband/hw/efa/efa_verbs.c +@@ -7,9 +7,9 @@ + #include + + #include +-#include + #include + #include ++#include + #include + + #include "efa.h" +--- a/drivers/infiniband/hw/hns/hns_roce_alloc.c ++++ b/drivers/infiniband/hw/hns/hns_roce_alloc.c +@@ -34,7 +34,7 @@ + #include + #include + #include "hns_roce_device.h" +-#include ++#include + + int hns_roce_bitmap_alloc(struct hns_roce_bitmap *bitmap, unsigned long *obj) + { +--- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c ++++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + #include + #include "i40iw.h" + +--- a/drivers/infiniband/hw/mlx4/mr.c ++++ b/drivers/infiniband/hw/mlx4/mr.c +@@ -33,6 +33,7 @@ + + #include + #include ++#include + + #include "mlx4_ib.h" + +--- a/drivers/infiniband/hw/mlx5/mem.c ++++ b/drivers/infiniband/hw/mlx5/mem.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include "mlx5_ib.h" + #include + +--- a/drivers/infiniband/hw/mthca/mthca_provider.c ++++ b/drivers/infiniband/hw/mthca/mthca_provider.c +@@ -35,8 +35,8 @@ + */ + + #include +-#include + #include ++#include + #include + + #include +--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c ++++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +@@ -44,9 +44,9 @@ + #include + #include + #include +-#include + #include + #include ++#include + #include + + #include "ocrdma.h" +--- a/drivers/infiniband/hw/qedr/verbs.c ++++ b/drivers/infiniband/hw/qedr/verbs.c +@@ -39,9 +39,9 @@ + #include + #include + #include +-#include + #include + #include ++#include + #include + + #include +--- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h ++++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma.h +@@ -53,8 +53,8 @@ + #include + #include + #include +-#include + #include ++#include + #include + + #include "pvrdma_ring.h" +--- a/include/rdma/ib_umem.h ++++ b/include/rdma/ib_umem.h +@@ -46,30 +46,6 @@ static inline size_t ib_umem_num_pages(s + { + return ib_umem_num_dma_blocks(umem, PAGE_SIZE); + } +- +-static inline void __rdma_umem_block_iter_start(struct ib_block_iter *biter, +- struct ib_umem *umem, +- unsigned long pgsz) +-{ +- __rdma_block_iter_start(biter, umem->sg_head.sgl, umem->nmap, pgsz); +-} +- +-/** +- * rdma_umem_for_each_dma_block - iterate over contiguous DMA blocks of the umem +- * @umem: umem to iterate over +- * @biter: block iterator variable +- * @pgsz: Page size to split the list into +- * +- * pgsz must be <= PAGE_SIZE or computed by ib_umem_find_best_pgsz(). The +- * returned DMA blocks will be aligned to pgsz and span the range: +- * ALIGN_DOWN(umem->address, pgsz) to ALIGN(umem->address + umem->length, pgsz) +- * +- * Performs exactly ib_umem_num_dma_blocks() iterations. +- */ +-#define rdma_umem_for_each_dma_block(umem, biter, pgsz) \ +- for (__rdma_umem_block_iter_start(biter, umem, pgsz); \ +- __rdma_block_iter_next(biter);) +- + #ifdef CONFIG_INFINIBAND_USER_MEM + + struct ib_umem *ib_umem_get(struct ib_device *device, unsigned long addr, +--- a/include/rdma/ib_verbs.h ++++ b/include/rdma/ib_verbs.h +@@ -2756,21 +2756,6 @@ struct ib_client { + u8 no_kverbs_req:1; + }; + +-/* +- * IB block DMA iterator +- * +- * Iterates the DMA-mapped SGL in contiguous memory blocks aligned +- * to a HW supported page size. +- */ +-struct ib_block_iter { +- /* internal states */ +- struct scatterlist *__sg; /* sg holding the current aligned block */ +- dma_addr_t __dma_addr; /* unaligned DMA address of this block */ +- unsigned int __sg_nents; /* number of SG entries */ +- unsigned int __sg_advance; /* number of bytes to advance in sg in next step */ +- unsigned int __pg_bit; /* alignment of current block */ +-}; +- + struct ib_device *_ib_alloc_device(size_t size); + #define ib_alloc_device(drv_struct, member) \ + container_of(_ib_alloc_device(sizeof(struct drv_struct) + \ +@@ -2792,38 +2777,6 @@ void ib_unregister_device_queued(struct + int ib_register_client (struct ib_client *client); + void ib_unregister_client(struct ib_client *client); + +-void __rdma_block_iter_start(struct ib_block_iter *biter, +- struct scatterlist *sglist, +- unsigned int nents, +- unsigned long pgsz); +-bool __rdma_block_iter_next(struct ib_block_iter *biter); +- +-/** +- * rdma_block_iter_dma_address - get the aligned dma address of the current +- * block held by the block iterator. +- * @biter: block iterator holding the memory block +- */ +-static inline dma_addr_t +-rdma_block_iter_dma_address(struct ib_block_iter *biter) +-{ +- return biter->__dma_addr & ~(BIT_ULL(biter->__pg_bit) - 1); +-} +- +-/** +- * rdma_for_each_block - iterate over contiguous memory blocks of the sg list +- * @sglist: sglist to iterate over +- * @biter: block iterator holding the memory block +- * @nents: maximum number of sg entries to iterate over +- * @pgsz: best HW supported page size to use +- * +- * Callers may use rdma_block_iter_dma_address() to get each +- * blocks aligned DMA address. +- */ +-#define rdma_for_each_block(sglist, biter, nents, pgsz) \ +- for (__rdma_block_iter_start(biter, sglist, nents, \ +- pgsz); \ +- __rdma_block_iter_next(biter);) +- + /** + * ib_get_client_data - Get IB client context + * @device:Device to get context for +--- /dev/null ++++ b/include/rdma/iter.h +@@ -0,0 +1,80 @@ ++/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */ ++/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. */ ++ ++#ifndef _RDMA_ITER_H_ ++#define _RDMA_ITER_H_ ++ ++#include ++#include ++ ++/** ++ * IB block DMA iterator ++ * ++ * Iterates the DMA-mapped SGL in contiguous memory blocks aligned ++ * to a HW supported page size. ++ */ ++struct ib_block_iter { ++ /* internal states */ ++ struct scatterlist *__sg; /* sg holding the current aligned block */ ++ dma_addr_t __dma_addr; /* unaligned DMA address of this block */ ++ unsigned int __sg_nents; /* number of SG entries */ ++ unsigned int __sg_advance; /* number of bytes to advance in sg in next step */ ++ unsigned int __pg_bit; /* alignment of current block */ ++}; ++ ++void __rdma_block_iter_start(struct ib_block_iter *biter, ++ struct scatterlist *sglist, ++ unsigned int nents, ++ unsigned long pgsz); ++bool __rdma_block_iter_next(struct ib_block_iter *biter); ++ ++/** ++ * rdma_block_iter_dma_address - get the aligned dma address of the current ++ * block held by the block iterator. ++ * @biter: block iterator holding the memory block ++ */ ++static inline dma_addr_t ++rdma_block_iter_dma_address(struct ib_block_iter *biter) ++{ ++ return biter->__dma_addr & ~(BIT_ULL(biter->__pg_bit) - 1); ++} ++ ++/** ++ * rdma_for_each_block - iterate over contiguous memory blocks of the sg list ++ * @sglist: sglist to iterate over ++ * @biter: block iterator holding the memory block ++ * @nents: maximum number of sg entries to iterate over ++ * @pgsz: best HW supported page size to use ++ * ++ * Callers may use rdma_block_iter_dma_address() to get each ++ * blocks aligned DMA address. ++ */ ++#define rdma_for_each_block(sglist, biter, nents, pgsz) \ ++ for (__rdma_block_iter_start(biter, sglist, nents, \ ++ pgsz); \ ++ __rdma_block_iter_next(biter);) ++ ++static inline void __rdma_umem_block_iter_start(struct ib_block_iter *biter, ++ struct ib_umem *umem, ++ unsigned long pgsz) ++{ ++ __rdma_block_iter_start(biter, umem->sg_head.sgl, umem->nmap, pgsz); ++} ++ ++/** ++ * rdma_umem_for_each_dma_block - iterate over contiguous DMA blocks of the umem ++ * @umem: umem to iterate over ++ * @biter: block iterator variable ++ * @pgsz: Page size to split the list into ++ * ++ * pgsz must be <= PAGE_SIZE or computed by ib_umem_find_best_pgsz(). The ++ * returned DMA blocks will be aligned to pgsz and span the range: ++ * ALIGN_DOWN(umem->address, pgsz) to ALIGN(umem->address + umem->length, pgsz) ++ * ++ * Performs exactly ib_umem_num_dma_blocks() iterations. ++ */ ++#define rdma_umem_for_each_dma_block(umem, biter, pgsz) \ ++ for (__rdma_umem_block_iter_start(biter, umem, pgsz); \ ++ __rdma_block_iter_next(biter);) ++ ++#endif /* _RDMA_ITER_H_ */ diff --git a/queue-5.10/rdma-umem-fix-kernel-doc-warnings.patch b/queue-5.10/rdma-umem-fix-kernel-doc-warnings.patch new file mode 100644 index 0000000000..24e311cc27 --- /dev/null +++ b/queue-5.10/rdma-umem-fix-kernel-doc-warnings.patch @@ -0,0 +1,41 @@ +From stable+bounces-263528-greg=kroah.com@vger.kernel.org Tue Jun 16 08:47:45 2026 +From: Sasha Levin +Date: Mon, 15 Jun 2026 23:17:35 -0400 +Subject: RDMA/umem: fix kernel-doc warnings +To: stable@vger.kernel.org +Cc: Randy Dunlap , Leon Romanovsky , Sasha Levin +Message-ID: <20260616031737.2781524-1-sashal@kernel.org> + +From: Randy Dunlap + +[ Upstream commit ff46d1392750444fab5ae5a0194764ffdc4ac0d2 ] + +Add or correct kernel-doc comments to eliminate warnings: + +Warning: include/rdma/ib_umem.h:104 function parameter 'biter' not + described in 'rdma_umem_for_each_dma_block' +Warning: include/rdma/ib_umem.h:140 function parameter 'pgsz_bitmap' not + described in 'ib_umem_find_best_pgoff' +Warning: include/rdma/ib_umem.h:141 No description found for return + value of 'ib_umem_find_best_pgoff' + +Signed-off-by: Randy Dunlap +Link: https://patch.msgid.link/20260224003120.3173892-1-rdunlap@infradead.org +Signed-off-by: Leon Romanovsky +Stable-dep-of: 15fe76e23615 ("RDMA/umem: Fix truncation for block sizes >= 4G") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/rdma/ib_umem.h | 1 + + 1 file changed, 1 insertion(+) + +--- a/include/rdma/ib_umem.h ++++ b/include/rdma/ib_umem.h +@@ -57,6 +57,7 @@ static inline void __rdma_umem_block_ite + /** + * rdma_umem_for_each_dma_block - iterate over contiguous DMA blocks of the umem + * @umem: umem to iterate over ++ * @biter: block iterator variable + * @pgsz: Page size to split the list into + * + * pgsz must be <= PAGE_SIZE or computed by ib_umem_find_best_pgsz(). The diff --git a/queue-5.10/rdma-umem-fix-truncation-for-block-sizes-4g.patch b/queue-5.10/rdma-umem-fix-truncation-for-block-sizes-4g.patch new file mode 100644 index 0000000000..4cdbb84386 --- /dev/null +++ b/queue-5.10/rdma-umem-fix-truncation-for-block-sizes-4g.patch @@ -0,0 +1,44 @@ +From stable+bounces-263529-greg=kroah.com@vger.kernel.org Tue Jun 16 08:47:48 2026 +From: Sasha Levin +Date: Mon, 15 Jun 2026 23:17:37 -0400 +Subject: RDMA/umem: Fix truncation for block sizes >= 4G +To: stable@vger.kernel.org +Cc: Jason Gunthorpe , Sasha Levin +Message-ID: <20260616031737.2781524-3-sashal@kernel.org> + +From: Jason Gunthorpe + +[ Upstream commit 15fe76e23615f502d051ef0768f86babaf08746c ] + +When the iommu is used the linearization of the mapping can give a single +block that is very large split across multiple SG entries. + +When __rdma_block_iter_next() reassembles the split SG entries it is +overflowing the 32 bit stack values and computed the wrong DMA addresses +for blocks after the truncation. + +Use the right types to hold DMA addresses. + +Link: https://patch.msgid.link/r/1-v1-88303e9e509f+f7-ib_umem_types_jgg@nvidia.com +Cc: stable@vger.kernel.org +Fixes: a808273a495c ("RDMA/verbs: Add a DMA iterator to return aligned contiguous memory blocks") +Signed-off-by: Jason Gunthorpe +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/infiniband/core/iter.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/infiniband/core/iter.c ++++ b/drivers/infiniband/core/iter.c +@@ -19,8 +19,8 @@ EXPORT_SYMBOL(__rdma_block_iter_start); + + bool __rdma_block_iter_next(struct ib_block_iter *biter) + { +- unsigned int block_offset; +- unsigned int delta; ++ dma_addr_t block_offset; ++ dma_addr_t delta; + + if (!biter->__sg_nents || !biter->__sg) + return false; diff --git a/queue-5.10/sched-use-u64-for-bandwidth-ratio-calculations.patch b/queue-5.10/sched-use-u64-for-bandwidth-ratio-calculations.patch new file mode 100644 index 0000000000..5ef2dde665 --- /dev/null +++ b/queue-5.10/sched-use-u64-for-bandwidth-ratio-calculations.patch @@ -0,0 +1,74 @@ +From stable+bounces-242630-greg=kroah.com@vger.kernel.org Sun May 3 06:10:20 2026 +From: Sasha Levin +Date: Sat, 2 May 2026 20:40:07 -0400 +Subject: sched: Use u64 for bandwidth ratio calculations +To: stable@vger.kernel.org +Cc: Joseph Salisbury , "Peter Zijlstra (Intel)" , Sasha Levin +Message-ID: <20260503004007.933354-1-sashal@kernel.org> + +From: Joseph Salisbury + +[ Upstream commit c6e80201e057dfb7253385e60bf541121bf5dc33 ] + +to_ratio() computes BW_SHIFT-scaled bandwidth ratios from u64 period and +runtime values, but it returns unsigned long. tg_rt_schedulable() also +stores the current group limit and the accumulated child sum in unsigned +long. + +On 32-bit builds, large bandwidth ratios can be truncated and the RT +group sum can wrap when enough siblings are present. That can let an +overcommitted RT hierarchy pass the schedulability check, and it also +narrows the helper result for other callers. + +Return u64 from to_ratio() and use u64 for the RT group totals so +bandwidth ratios are preserved and compared at full width on both 32-bit +and 64-bit builds. + +Fixes: b40b2e8eb521 ("sched: rt: multi level group constraints") +Assisted-by: Codex:GPT-5 +Signed-off-by: Joseph Salisbury +Signed-off-by: Peter Zijlstra (Intel) +Cc: stable@vger.kernel.org +Link: https://patch.msgid.link/20260403210014.2713404-1-joseph.salisbury@oracle.com +[ dropped `extern` keyword from `to_ratio()` declaration ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/sched/core.c | 2 +- + kernel/sched/rt.c | 2 +- + kernel/sched/sched.h | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -3328,7 +3328,7 @@ void sched_post_fork(struct task_struct + uclamp_post_fork(p); + } + +-unsigned long to_ratio(u64 period, u64 runtime) ++u64 to_ratio(u64 period, u64 runtime) + { + if (runtime == RUNTIME_INF) + return BW_UNIT; +--- a/kernel/sched/rt.c ++++ b/kernel/sched/rt.c +@@ -2527,7 +2527,7 @@ static int tg_rt_schedulable(struct task + { + struct rt_schedulable_data *d = data; + struct task_group *child; +- unsigned long total, sum = 0; ++ u64 total, sum = 0; + u64 period, runtime; + + period = ktime_to_ns(tg->rt_bandwidth.rt_period); +--- a/kernel/sched/sched.h ++++ b/kernel/sched/sched.h +@@ -1956,7 +1956,7 @@ extern void init_dl_inactive_task_timer( + #define RATIO_SHIFT 8 + #define MAX_BW_BITS (64 - BW_SHIFT) + #define MAX_BW ((1ULL << MAX_BW_BITS) - 1) +-unsigned long to_ratio(u64 period, u64 runtime); ++u64 to_ratio(u64 period, u64 runtime); + + extern void init_entity_runnable_average(struct sched_entity *se); + extern void post_init_entity_util_avg(struct task_struct *p); diff --git a/queue-5.10/scsi-target-iscsi-bound-iscsi_encode_text_output-appends-to-rsp_buf.patch b/queue-5.10/scsi-target-iscsi-bound-iscsi_encode_text_output-appends-to-rsp_buf.patch new file mode 100644 index 0000000000..34985d1388 --- /dev/null +++ b/queue-5.10/scsi-target-iscsi-bound-iscsi_encode_text_output-appends-to-rsp_buf.patch @@ -0,0 +1,203 @@ +From stable+bounces-260836-greg=kroah.com@vger.kernel.org Sat Jun 6 07:48:40 2026 +From: Sasha Levin +Date: Fri, 5 Jun 2026 22:18:34 -0400 +Subject: scsi: target: iscsi: Bound iscsi_encode_text_output() appends to rsp_buf +To: stable@vger.kernel.org +Cc: Michael Bommarito , John Garry , "Martin K. Petersen" , Sasha Levin +Message-ID: <20260606021834.2487480-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit bf33e01f88388c43e285492a63e539df6ffed64c ] + +iscsi_encode_text_output() concatenates "key=value\0" records into +login->rsp_buf, an 8192-byte kzalloc(MAX_KEY_VALUE_PAIRS) buffer +allocated in iscsit_alloc_login_setup_buffer(). The three sprintf() call +sites in this function (lines 1398, 1411, 1424 in v7.1-rc2) never check +the remaining buffer capacity: + + *length += sprintf(output_buf, "%s=%s", er->key, er->value); + *length += 1; + output_buf = textbuf + *length; + +The 8192-byte ceiling at iscsi_target_check_login_request() bounds the +*input* Login PDU payload, but a single PDU can carry up to 2048 minimal +four-byte "a=b\0" pairs, each unknown key expanding to a 16-byte +"a=NotUnderstood\0" output record via iscsi_add_notunderstood_response(). +2048 * 16 = 32 KiB of output into an 8 KiB buffer, producing a ~24 KiB +heap overrun in the kmalloc-8k slab. + +The fix introduces a static iscsi_encode_text_record() helper that uses +snprintf() with a per-call bounds check against the remaining buffer, +and threads a u32 textbuf_size parameter through +iscsi_encode_text_output(). Both call sites in +iscsi_target_handle_csg_zero() (PHASE_SECURITY) and +iscsi_target_handle_csg_one() (PHASE_OPERATIONAL) pass +MAX_KEY_VALUE_PAIRS. On overflow the encoder logs the condition, calls +iscsi_release_extra_responses() to drop queued records, and returns -1; +both caller sites now emit ISCSI_STATUS_CLS_INITIATOR_ERR / +ISCSI_LOGIN_STATUS_INIT_ERR via iscsit_tx_login_rsp() before returning, +so the initiator sees an explicit failed-login response rather than a +silent connection drop. (Prior to this patch only the PHASE_OPERATIONAL +caller did that; the PHASE_SECURITY caller is converted to the same +shape.) + +Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Michael Bommarito +Tested-by: John Garry +Reviewed-by: John Garry +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/target/iscsi/iscsi_target_nego.c | 7 ++ + drivers/target/iscsi/iscsi_target_parameters.c | 62 +++++++++++++++++++------ + drivers/target/iscsi/iscsi_target_parameters.h | 2 + 3 files changed, 55 insertions(+), 16 deletions(-) + +--- a/drivers/target/iscsi/iscsi_target_nego.c ++++ b/drivers/target/iscsi/iscsi_target_nego.c +@@ -878,10 +878,14 @@ static int iscsi_target_handle_csg_zero( + SENDER_TARGET, + login->rsp_buf, + &login->rsp_length, ++ MAX_KEY_VALUE_PAIRS, + conn->param_list, + conn->tpg->tpg_attrib.login_keys_workaround); +- if (ret < 0) ++ if (ret < 0) { ++ iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ++ ISCSI_LOGIN_STATUS_INIT_ERR); + return -1; ++ } + + if (!iscsi_check_negotiated_keys(conn->param_list)) { + if (conn->tpg->tpg_attrib.authentication && +@@ -949,6 +953,7 @@ static int iscsi_target_handle_csg_one(s + SENDER_TARGET, + login->rsp_buf, + &login->rsp_length, ++ MAX_KEY_VALUE_PAIRS, + conn->param_list, + conn->tpg->tpg_attrib.login_keys_workaround); + if (ret < 0) { +--- a/drivers/target/iscsi/iscsi_target_parameters.c ++++ b/drivers/target/iscsi/iscsi_target_parameters.c +@@ -1421,19 +1421,42 @@ free_buffer: + return -1; + } + ++/* ++ * Append "key=value" plus a trailing NUL into @textbuf at *@length. ++ * Returns 0 on success and advances *@length, or -EMSGSIZE if the ++ * record (including the NUL) would not fit in the remaining buffer. ++ */ ++static int iscsi_encode_text_record(char *textbuf, u32 *length, ++ u32 textbuf_size, ++ const char *key, const char *value) ++{ ++ int n; ++ u32 avail; ++ ++ if (*length >= textbuf_size) ++ return -EMSGSIZE; ++ ++ avail = textbuf_size - *length; ++ n = snprintf(textbuf + *length, avail, "%s=%s", key, value); ++ if (n < 0 || (u32)n + 1 > avail) ++ return -EMSGSIZE; ++ ++ *length += n + 1; ++ return 0; ++} ++ + int iscsi_encode_text_output( + u8 phase, + u8 sender, + char *textbuf, + u32 *length, ++ u32 textbuf_size, + struct iscsi_param_list *param_list, + bool keys_workaround) + { +- char *output_buf = NULL; + struct iscsi_extra_response *er; + struct iscsi_param *param; +- +- output_buf = textbuf + *length; ++ int ret; + + if (iscsi_enforce_integrity_rules(phase, param_list) < 0) + return -1; +@@ -1445,10 +1468,12 @@ int iscsi_encode_text_output( + !IS_PSTATE_RESPONSE_SENT(param) && + !IS_PSTATE_REPLY_OPTIONAL(param) && + (param->phase & phase)) { +- *length += sprintf(output_buf, "%s=%s", +- param->name, param->value); +- *length += 1; +- output_buf = textbuf + *length; ++ ret = iscsi_encode_text_record(textbuf, length, ++ textbuf_size, ++ param->name, ++ param->value); ++ if (ret < 0) ++ goto err_overflow; + SET_PSTATE_RESPONSE_SENT(param); + pr_debug("Sending key: %s=%s\n", + param->name, param->value); +@@ -1458,10 +1483,12 @@ int iscsi_encode_text_output( + !IS_PSTATE_ACCEPTOR(param) && + !IS_PSTATE_PROPOSER(param) && + (param->phase & phase)) { +- *length += sprintf(output_buf, "%s=%s", +- param->name, param->value); +- *length += 1; +- output_buf = textbuf + *length; ++ ret = iscsi_encode_text_record(textbuf, length, ++ textbuf_size, ++ param->name, ++ param->value); ++ if (ret < 0) ++ goto err_overflow; + SET_PSTATE_PROPOSER(param); + iscsi_check_proposer_for_optional_reply(param, + keys_workaround); +@@ -1471,14 +1498,21 @@ int iscsi_encode_text_output( + } + + list_for_each_entry(er, ¶m_list->extra_response_list, er_list) { +- *length += sprintf(output_buf, "%s=%s", er->key, er->value); +- *length += 1; +- output_buf = textbuf + *length; ++ ret = iscsi_encode_text_record(textbuf, length, textbuf_size, ++ er->key, er->value); ++ if (ret < 0) ++ goto err_overflow; + pr_debug("Sending key: %s=%s\n", er->key, er->value); + } + iscsi_release_extra_responses(param_list); + + return 0; ++ ++err_overflow: ++ pr_err("iSCSI login response buffer (%u bytes) exhausted, dropping login.\n", ++ textbuf_size); ++ iscsi_release_extra_responses(param_list); ++ return -1; + } + + int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list) +--- a/drivers/target/iscsi/iscsi_target_parameters.h ++++ b/drivers/target/iscsi/iscsi_target_parameters.h +@@ -46,7 +46,7 @@ extern struct iscsi_param *iscsi_find_pa + extern int iscsi_extract_key_value(char *, char **, char **); + extern int iscsi_update_param_value(struct iscsi_param *, char *); + extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsi_conn *); +-extern int iscsi_encode_text_output(u8, u8, char *, u32 *, ++extern int iscsi_encode_text_output(u8, u8, char *, u32 *, u32, + struct iscsi_param_list *, bool); + extern int iscsi_check_negotiated_keys(struct iscsi_param_list *); + extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *, diff --git a/queue-5.10/scsi-target-iscsi-fix-crc-overread-and-double-free-in-iscsit_handle_text_cmd.patch b/queue-5.10/scsi-target-iscsi-fix-crc-overread-and-double-free-in-iscsit_handle_text_cmd.patch new file mode 100644 index 0000000000..a821043b40 --- /dev/null +++ b/queue-5.10/scsi-target-iscsi-fix-crc-overread-and-double-free-in-iscsit_handle_text_cmd.patch @@ -0,0 +1,124 @@ +From stable+bounces-260882-greg=kroah.com@vger.kernel.org Sat Jun 6 18:36:49 2026 +From: Sasha Levin +Date: Sat, 6 Jun 2026 09:05:41 -0400 +Subject: scsi: target: iscsi: Fix CRC overread and double-free in iscsit_handle_text_cmd() +To: stable@vger.kernel.org +Cc: Michael Bommarito , John Garry , "Martin K. Petersen" , Sasha Levin +Message-ID: <20260606130541.2924890-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit 778c2ab142c625a8a8afa570e0f9b7873f445d99 ] + +Two latent bugs in the Text-phase handler, both present since the +original LIO integration in commit e48354ce078c ("iscsi-target: Add +iSCSI fabric support for target v4.1"): + +1) DataDigest CRC buffer overread (4 bytes past text_in). + + text_in is kzalloc()'d at ALIGN(payload_length, 4). rx_size is then + incremented by ISCSI_CRC_LEN to make room for the received DataDigest + in the iovec, but the same (now-bumped) rx_size is passed as the + buffer length to iscsit_crc_buf(): + + if (conn->conn_ops->DataDigest) { + ... + rx_size += ISCSI_CRC_LEN; + } + ... + if (conn->conn_ops->DataDigest) { + data_crc = iscsit_crc_buf(text_in, rx_size, 0, NULL); + + iscsit_crc_buf() walks rx_size bytes of text_in with crc32c(), so + when DataDigest is negotiated it reads 4 bytes past the end of the + text_in allocation. KASAN reproduces this directly on the unpatched + mainline tree as slab-out-of-bounds in crc32c() called from the Text + PDU path. The OOB bytes feed crc32c() and are then compared against + the initiator-supplied checksum, so the value does not flow back to + the attacker, but the kernel does read past the buffer on every Text + PDU with DataDigest=CRC32C. + + Fix by passing the actual padded payload length + (ALIGN(payload_length, 4)) that was used for the kzalloc(). + +2) Stale cmd->text_in_ptr re-free (double-free) on ERL>0 bad DataDigest + drop. + + On DataDigest mismatch with ErrorRecoveryLevel > 0 the handler + silently drops the PDU and lets the initiator plug the CmdSN gap: + + kfree(text_in); + return 0; + + cmd->text_in_ptr still points at the freed buffer. The next Text + Request on the same ITT re-enters iscsit_setup_text_cmd(), which + unconditionally does + + kfree(cmd->text_in_ptr); + cmd->text_in_ptr = NULL; + + freeing the same pointer a second time. Session teardown via + iscsit_release_cmd() has the same shape and hits the same double-free + if the connection is dropped before a second Text Request arrives. + + On an unmodified mainline tree the bug-1 CRC overread fires first on + the initial valid Text Request and perturbs the subsequent state, so + #4 was isolated by building a kernel with only the bug-1 hunk of this + patch applied plus temporary printk() observability around the three + relevant kfree() sites. The observability prints are not part of + this patch. On that build, a three-PDU Text Request sequence after + login produces two back-to-back splats: + + BUG: KASAN: double-free in iscsit_setup_text_cmd+0x?? + BUG: KASAN: double-free in iscsit_release_cmd+0x?? + + showing the same pointer freed in the ERL>0 drop path and again in + iscsit_setup_text_cmd() (next Text Request on the same ITT) and once + more in iscsit_release_cmd() (session teardown). On distro kernels + with CONFIG_SLAB_FREELIST_HARDENED=y (default) the double-free + becomes a remote kernel BUG(); on non-hardened kernels it corrupts + the slab freelist. + + Fix by clearing cmd->text_in_ptr after the kfree() in the ERL>0 drop + path. With both hunks applied #4 is directly observable on the stock + tree without observability printks; fixing bug-1 alone would mask #4 + less, not more, so the hunks are submitted together. + +Both fixes are one-liners. The Text PDU state machine is unchanged and +the wire protocol is unaffected. + +Fixes: e48354ce078c ("iscsi-target: Add iSCSI fabric support for target v4.1") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-7 +Signed-off-by: Michael Bommarito +Tested-by: John Garry +Reviewed-by: John Garry +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/target/iscsi/iscsi_target.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- a/drivers/target/iscsi/iscsi_target.c ++++ b/drivers/target/iscsi/iscsi_target.c +@@ -2294,8 +2294,9 @@ iscsit_handle_text_cmd(struct iscsi_conn + + if (conn->conn_ops->DataDigest) { + iscsit_do_crypto_hash_buf(conn->conn_rx_hash, +- text_in, rx_size, 0, NULL, +- &data_crc); ++ text_in, ++ ALIGN(payload_length, 4), ++ 0, NULL, &data_crc); + + if (checksum != data_crc) { + pr_err("Text data CRC32C DataDigest" +@@ -2315,6 +2316,7 @@ iscsit_handle_text_cmd(struct iscsi_conn + " Command CmdSN: 0x%08x due to" + " DataCRC error.\n", hdr->cmdsn); + kfree(text_in); ++ cmd->text_in_ptr = NULL; + return 0; + } + } else { diff --git a/queue-5.10/selftests-mptcp-drop-nanoseconds-width-specifier.patch b/queue-5.10/selftests-mptcp-drop-nanoseconds-width-specifier.patch new file mode 100644 index 0000000000..4526f8242c --- /dev/null +++ b/queue-5.10/selftests-mptcp-drop-nanoseconds-width-specifier.patch @@ -0,0 +1,69 @@ +From stable+bounces-256922-greg=kroah.com@vger.kernel.org Sat May 30 21:10:44 2026 +From: Sasha Levin +Date: Sat, 30 May 2026 11:40:36 -0400 +Subject: selftests: mptcp: drop nanoseconds width specifier +To: stable@vger.kernel.org +Cc: "Matthieu Baerts (NGI0)" , Paolo Abeni , Sasha Levin +Message-ID: <20260530154036.2741872-1-sashal@kernel.org> + +From: "Matthieu Baerts (NGI0)" + +[ Upstream commit 01ff78e4b3d98689184c52d97f9575dfbdc3b10f ] + +Using the format specifier +%s%3N with GNU date is honoured, and only +prints 3 digits of the nanoseconds portion of the seconds since epoch, +which corresponds to the milliseconds. + +The uutils implementation of date currently does not honour this, and +always prints all 9 digits. This is a known issue [1], but can be worked +around by adapting this test to use nanoseconds instead of microseconds, +and then divide it by 1e6. + +This fix is similar to what has been done on systemd side [2], and it is +needed to run the selftests on Ubuntu 26.04, containing uutils 0.8.0. + +Note that the Fixes tag is there even if this patch doesn't fix an issue +in the kernel selftests, but it is useful for those using uutils 0.8.0. + +Fixes: 048d19d444be ("mptcp: add basic kselftest for mptcp") +Cc: stable@vger.kernel.org +Link: https://github.com/uutils/coreutils/issues/11658 [1] +Link: https://github.com/systemd/systemd/pull/41627 [2] +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260515-net-mptcp-misc-fixes-7-1-rc4-v2-6-701e96419f2f@kernel.org +Signed-off-by: Paolo Abeni +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/net/mptcp/mptcp_connect.sh | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/tools/testing/selftests/net/mptcp/mptcp_connect.sh ++++ b/tools/testing/selftests/net/mptcp/mptcp_connect.sh +@@ -421,7 +421,7 @@ do_transfer() + wait_local_port_listen "${listener_ns}" "${port}" + + local start +- start=$(date +%s%3N) ++ start=$(date +%s%N) + ip netns exec ${connector_ns} ./mptcp_connect -t $timeout -p $port -s ${cl_proto} $extra_args $connect_addr < "$cin" > "$cout" & + local cpid=$! + +@@ -431,7 +431,7 @@ do_transfer() + local rets=$? + + local stop +- stop=$(date +%s%3N) ++ stop=$(date +%s%N) + + if $capture; then + sleep 1 +@@ -440,7 +440,7 @@ do_transfer() + fi + + local duration +- duration=$((stop-start)) ++ duration=$(((stop-start) / 1000000)) + duration=$(printf "(duration %05sms)" $duration) + if [ ${rets} -ne 0 ] || [ ${retc} -ne 0 ]; then + echo "$duration [ FAIL ] client exit code $retc, server $rets" 1>&2 diff --git a/queue-5.10/serial-altera_jtaguart-handle-uart_add_one_port-failures.patch b/queue-5.10/serial-altera_jtaguart-handle-uart_add_one_port-failures.patch new file mode 100644 index 0000000000..6b2afe4080 --- /dev/null +++ b/queue-5.10/serial-altera_jtaguart-handle-uart_add_one_port-failures.patch @@ -0,0 +1,59 @@ +From stable+bounces-260876-greg=kroah.com@vger.kernel.org Sat Jun 6 18:03:46 2026 +From: Sasha Levin +Date: Sat, 6 Jun 2026 08:32:04 -0400 +Subject: serial: altera_jtaguart: handle uart_add_one_port() failures +To: stable@vger.kernel.org +Cc: Myeonghun Pak , stable , Ijae Kim , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260606123204.2861133-2-sashal@kernel.org> + +From: Myeonghun Pak + +[ Upstream commit ea66be25f0e934f49d24cd0c5845d13cdba3520b ] + +altera_jtaguart_probe() maps the register window before registering the +UART port, but it ignores failures from uart_add_one_port(). If port +registration fails, probe still returns success and the mapping remains +live until a later remove path that is not part of probe failure cleanup. + +Return the uart_add_one_port() error and unmap the register window on +that failure path. + +This issue was identified during our ongoing static-analysis research while +reviewing kernel code. + +Fixes: 5bcd601049c6 ("serial: Add driver for the Altera JTAG UART") +Cc: stable +Co-developed-by: Ijae Kim +Signed-off-by: Ijae Kim +Signed-off-by: Myeonghun Pak +Link: https://patch.msgid.link/20260512065837.79528-1-mhun512@gmail.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/altera_jtaguart.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/tty/serial/altera_jtaguart.c ++++ b/drivers/tty/serial/altera_jtaguart.c +@@ -423,6 +423,7 @@ static int altera_jtaguart_probe(struct + struct resource *res_mem; + int i = pdev->id; + int irq; ++ int ret; + + /* -1 emphasizes that the platform must have one port, no .N suffix */ + if (i == -1) +@@ -462,7 +463,11 @@ static int altera_jtaguart_probe(struct + port->flags = UPF_BOOT_AUTOCONF; + port->dev = &pdev->dev; + +- uart_add_one_port(&altera_jtaguart_driver, port); ++ ret = uart_add_one_port(&altera_jtaguart_driver, port); ++ if (ret) { ++ iounmap(port->membase); ++ return ret; ++ } + + return 0; + } diff --git a/queue-5.10/serial-altera_jtaguart-use-platform_get_irq_optional-to-get-the-interrupt.patch b/queue-5.10/serial-altera_jtaguart-use-platform_get_irq_optional-to-get-the-interrupt.patch new file mode 100644 index 0000000000..d214826c0a --- /dev/null +++ b/queue-5.10/serial-altera_jtaguart-use-platform_get_irq_optional-to-get-the-interrupt.patch @@ -0,0 +1,59 @@ +From stable+bounces-260875-greg=kroah.com@vger.kernel.org Sat Jun 6 18:03:36 2026 +From: Sasha Levin +Date: Sat, 6 Jun 2026 08:32:03 -0400 +Subject: serial: altera_jtaguart: Use platform_get_irq_optional() to get the interrupt +To: stable@vger.kernel.org +Cc: Lad Prabhakar , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260606123204.2861133-1-sashal@kernel.org> + +From: Lad Prabhakar + +[ Upstream commit 60302276caff50f907bc3391a364691ab4a21b43 ] + +platform_get_resource(pdev, IORESOURCE_IRQ, ..) relies on static +allocation of IRQ resources in DT core code, this causes an issue +when using hierarchical interrupt domains using "interrupts" property +in the node as this bypasses the hierarchical setup and messes up the +irq chaining. + +In preparation for removal of static setup of IRQ resource from DT core +code use platform_get_irq_optional(). + +Signed-off-by: Lad Prabhakar +Link: https://lore.kernel.org/r/20211224142917.6966-7-prabhakar.mahadev-lad.rj@bp.renesas.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: ea66be25f0e9 ("serial: altera_jtaguart: handle uart_add_one_port() failures") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/altera_jtaguart.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +--- a/drivers/tty/serial/altera_jtaguart.c ++++ b/drivers/tty/serial/altera_jtaguart.c +@@ -420,8 +420,9 @@ static int altera_jtaguart_probe(struct + struct altera_jtaguart_platform_uart *platp = + dev_get_platdata(&pdev->dev); + struct uart_port *port; +- struct resource *res_irq, *res_mem; ++ struct resource *res_mem; + int i = pdev->id; ++ int irq; + + /* -1 emphasizes that the platform must have one port, no .N suffix */ + if (i == -1) +@@ -440,9 +441,11 @@ static int altera_jtaguart_probe(struct + else + return -ENODEV; + +- res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +- if (res_irq) +- port->irq = res_irq->start; ++ irq = platform_get_irq_optional(pdev, 0); ++ if (irq < 0 && irq != -ENXIO) ++ return irq; ++ if (irq > 0) ++ port->irq = irq; + else if (platp) + port->irq = platp->irq; + else diff --git a/queue-5.10/serial-qcom-geni-fix-uart_rx_par_en-bit-position.patch b/queue-5.10/serial-qcom-geni-fix-uart_rx_par_en-bit-position.patch new file mode 100644 index 0000000000..02229e3db1 --- /dev/null +++ b/queue-5.10/serial-qcom-geni-fix-uart_rx_par_en-bit-position.patch @@ -0,0 +1,42 @@ +From stable+bounces-260874-greg=kroah.com@vger.kernel.org Sat Jun 6 18:03:16 2026 +From: Sasha Levin +Date: Sat, 6 Jun 2026 08:32:00 -0400 +Subject: serial: qcom-geni: fix UART_RX_PAR_EN bit position +To: stable@vger.kernel.org +Cc: Prasanna S , stable , Konrad Dybcio , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260606123200.2861082-3-sashal@kernel.org> + +From: Prasanna S + +[ Upstream commit ca2584d841b69391ffc4144840563d2e1a0018df ] + +UART_RX_PAR_EN is incorrectly defined as bit 3, which triggers false +framing errors (S_GP_IRQ_1_EN) and causes received data to be dropped +when parity is enabled and the parity bit is 0. + +Define UART_RX_PAR_EN as bit 4 of the SE_UART_RX_TRANS_CFG register, as +specified in the reference manual. + +Fixes: c4f528795d1a ("tty: serial: msm_geni_serial: Add serial driver support for GENI based QUP") +Cc: stable +Signed-off-by: Prasanna S +Reviewed-by: Konrad Dybcio +Link: https://patch.msgid.link/20260428-serial-bit-correct-v1-1-9131ad5b97d8@oss.qualcomm.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/qcom_geni_serial.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/tty/serial/qcom_geni_serial.c ++++ b/drivers/tty/serial/qcom_geni_serial.c +@@ -43,7 +43,7 @@ + #define TX_STOP_BIT_LEN_2 2 + + /* SE_UART_RX_TRANS_CFG */ +-#define UART_RX_PAR_EN BIT(3) ++#define UART_RX_PAR_EN BIT(4) + + /* SE_UART_RX_WORD_LEN */ + #define RX_WORD_LEN_MASK GENMASK(9, 0) diff --git a/queue-5.10/series b/queue-5.10/series index 5ec666068e..076feaadec 100644 --- a/queue-5.10/series +++ b/queue-5.10/series @@ -215,3 +215,102 @@ io_uring-poll-fix-signed-comparison-in-io_poll_get_ownership.patch ipvs-skip-ipv6-extension-headers-for-csum-checks.patch batman-adv-stop-tp_meter-sessions-during-mesh-teardown.patch batman-adv-tp_meter-fix-tp_num-leak-on-kmalloc-failure.patch +f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch +smb-client-require-a-full-nfs-mode-sid-before-reading-mode-bits.patch +smb-client-fix-oob-read-in-smb2_ioctl_query_info-query_info-path.patch +net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch +drm-nouveau-fix-u32-overflow-in-pushbuf-reloc-bounds-check.patch +arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch +thermal-core-fix-thermal-zone-governor-cleanup-issues.patch +wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch +alsa-aoa-use-guard-for-mutex-locks.patch +alsa-aoa-i2sbus-clear-stale-prepared-state.patch +media-rc-ttusbir-respect-dma-coherency-rules.patch +alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch +erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch +media-rc-igorplugusb-heed-coherency-rules.patch +sched-use-u64-for-bandwidth-ratio-calculations.patch +alsa-core-fix-potential-data-race-at-fasync-handling.patch +net-qrtr-ns-limit-the-maximum-number-of-lookups.patch +net-qrtr-ns-change-servers-radix-tree-to-xarray.patch +net-qrtr-ns-free-the-node-during-ctrl_cmd_bye.patch +net-qrtr-ns-limit-the-total-number-of-nodes.patch +net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch +mtd-spi-nor-sst-fix-write-enable-before-aai-sequence.patch +udf-fix-partition-descriptor-append-bookkeeping.patch +hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch +hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch +bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.patch +can-ucan-fix-typos-in-comments.patch +can-ucan-fix-devres-lifetime.patch +ktest-fixing-indentation-to-match-expected-pattern.patch +ktest-fix-the-month-in-the-name-of-the-failure-directory.patch +ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch +alsa-aloop-fix-peer-runtime-uaf-during-format-change-stop.patch +printk-add-print_hex_dump_devel.patch +crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch +acpi-scan-use-acpi_dev_put-in-object-add-error-paths.patch +tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch +wifi-brcmfmac-fix-potential-use-after-free-issue-when-stopping-watchdog-task.patch +usb-dwc3-move-guid-programming-after-phy-initialization.patch +spi-syncuacer-fix-controller-deregistration.patch +spi-sun4i-fix-controller-deregistration.patch +spi-ti-qspi-fix-controller-deregistration.patch +spi-zynq-qspi-fix-controller-deregistration.patch +spi-sun6i-fix-controller-deregistration.patch +spi-tegra20-sflash-fix-controller-deregistration.patch +spi-tegra114-fix-controller-deregistration.patch +spi-uniphier-fix-controller-deregistration.patch +mm-hugetlb_cma-round-up-per_node-before-logging-it.patch +fbcon-avoid-oob-font-access-if-console-rotation-fails.patch +spi-topcliff-pch-fix-controller-deregistration.patch +btrfs-fix-btrfs_ioctl_space_info-slot_count-toctou-which-can-lead-to-info-leak.patch +tracing-probes-limit-size-of-event-probe-to-3k.patch +pmdomain-core-fix-detach-procedure-for-virtual-devices-in-genpd.patch +dm-btree-improve-btree-residency.patch +dm-thin-fix-metadata-refcount-underflow.patch +btrfs-fix-missing-last_unlink_trans-update-when-removing-a-directory.patch +smb-client-use-fullsessionkey-for-aes-256-encryption-key-derivation.patch +mptcp-pm-add_addr-rtx-fix-potential-data-race.patch +f2fs-fix-incorrect-file-address-mapping-when-inline-inode-is-unwritten.patch +spi-st-ssc4-fix-controller-deregistration.patch +spi-lantiq-ssc-fix-controller-deregistration.patch +bluetooth-fix-uaf-in-l2cap_sock_cleanup_listen-vs-l2cap_conn_del.patch +bluetooth-hci_qca-convert-timeout-from-jiffies-to-ms.patch +qed-use-the-bitmap-api-to-simplify-some-functions.patch +qed-fix-double-free-in-qed_cxt_tables_alloc.patch +net-remove-redundant-if-statements.patch +netfilter-nf_queue-hold-bridge-skb-dev-while-queued.patch +bluetooth-consolidate-code-around-sk_alloc-into-a-helper-function.patch +bluetooth-init-sk_peer_-on-bt_sock_alloc.patch +bluetooth-serialize-accept_q-access.patch +net-hsr-defer-node-table-free-until-after-rcu-readers.patch +ice-fix-vf-queue-configuration-with-low-mtu-values.patch +use-less-confusing-names-for-iov_iter-direction-initializers.patch +selftests-mptcp-drop-nanoseconds-width-specifier.patch +mptcp-do-not-drop-partial-packets.patch +octeontx2-pf-avoid-double-free-of-pool-stack-on-aq-init-failure.patch +octeontx2-af-add-validation-for-lmac-type.patch +octeontx2-af-replace-deprecated-strncpy-with-strscpy.patch +octeontx2-af-cgx-add-bounds-check-to-cgx_speed_mbps-index.patch +spi-qup-switch-to-use-modern-name.patch +spi-qup-fix-error-pointer-deref-after-dma-setup-failure.patch +arm64-tlb-flush-walk-cache-when-unsharing-pmd-tables.patch +phy-tegra-xusb-disable-trk-clk-when-not-in-use.patch +phy-tegra-xusb-fix-per-pad-high-speed-termination-calibration.patch +scsi-target-iscsi-bound-iscsi_encode_text_output-appends-to-rsp_buf.patch +usb-typec-ucsi-check-if-power-role-change-actually-happened-before-handling.patch +thunderbolt-property-cap-recursion-depth-in-__tb_property_parse_dir.patch +tty-serial-qcom-geni-serial-remove-unused-symbols.patch +tty-serial-qcom-geni-serial-align-define-values.patch +serial-qcom-geni-fix-uart_rx_par_en-bit-position.patch +serial-altera_jtaguart-use-platform_get_irq_optional-to-get-the-interrupt.patch +serial-altera_jtaguart-handle-uart_add_one_port-failures.patch +scsi-target-iscsi-fix-crc-overread-and-double-free-in-iscsit_handle_text_cmd.patch +usb-typec-ucsi-don-t-update-power_supply-on-power-role-change-if-not-connected.patch +netfilter-nft_fib-fix-stale-stack-leak-via-the-oifname-register.patch +hv_netvsc-use-kmap_local_page-in-netvsc_copy_to_send_buf.patch +mm-huge_memory-update-file-pmd-counter-before-folio_put.patch +rdma-umem-fix-kernel-doc-warnings.patch +rdma-move-dma-block-iterator-logic-into-dedicated-files.patch +rdma-umem-fix-truncation-for-block-sizes-4g.patch diff --git a/queue-5.10/smb-client-fix-oob-read-in-smb2_ioctl_query_info-query_info-path.patch b/queue-5.10/smb-client-fix-oob-read-in-smb2_ioctl_query_info-query_info-path.patch new file mode 100644 index 0000000000..398196f59f --- /dev/null +++ b/queue-5.10/smb-client-fix-oob-read-in-smb2_ioctl_query_info-query_info-path.patch @@ -0,0 +1,55 @@ +From stable+bounces-241001-greg=kroah.com@vger.kernel.org Fri Apr 24 21:50:23 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 12:20:14 -0400 +Subject: smb: client: fix OOB read in smb2_ioctl_query_info QUERY_INFO path +To: stable@vger.kernel.org +Cc: Michael Bommarito , Steve French , Sasha Levin +Message-ID: <20260424162014.2284352-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit a58c5af19ff0d6f44f6e9fe31e33a2c92223f77e ] + +smb2_ioctl_query_info() has two response-copy branches: PASSTHRU_FSCTL +and the default QUERY_INFO path. The QUERY_INFO branch clamps +qi.input_buffer_length to the server-reported OutputBufferLength and then +copies qi.input_buffer_length bytes from qi_rsp->Buffer to userspace, but +it never verifies that the flexible-array payload actually fits within +rsp_iov[1].iov_len. + +A malicious server can return OutputBufferLength larger than the actual +QUERY_INFO response, causing copy_to_user() to walk past the response +buffer and expose adjacent kernel heap to userspace. + +Guard the QUERY_INFO copy with a bounds check on the actual Buffer +payload. Use struct_size(qi_rsp, Buffer, qi.input_buffer_length) +rather than an open-coded addition so the guard cannot overflow on +32-bit builds. + +Fixes: f5778c398713 ("SMB3: Allow SMB3 FSCTL queries to be sent to server from tools") +Cc: stable@vger.kernel.org +Signed-off-by: Michael Bommarito +Assisted-by: Claude:claude-opus-4-6 +Assisted-by: Codex:gpt-5-4 +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/smb2ops.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -1720,6 +1720,12 @@ smb2_ioctl_query_info(const unsigned int + qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base; + if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length) + qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength); ++ if (qi.input_buffer_length > 0 && ++ struct_size(qi_rsp, Buffer, qi.input_buffer_length) > ++ rsp_iov[1].iov_len) { ++ rc = -EFAULT; ++ goto out; ++ } + if (copy_to_user(&pqi->input_buffer_length, + &qi.input_buffer_length, + sizeof(qi.input_buffer_length))) { diff --git a/queue-5.10/smb-client-require-a-full-nfs-mode-sid-before-reading-mode-bits.patch b/queue-5.10/smb-client-require-a-full-nfs-mode-sid-before-reading-mode-bits.patch new file mode 100644 index 0000000000..145a9d4c74 --- /dev/null +++ b/queue-5.10/smb-client-require-a-full-nfs-mode-sid-before-reading-mode-bits.patch @@ -0,0 +1,46 @@ +From stable+bounces-240986-greg=kroah.com@vger.kernel.org Fri Apr 24 20:16:02 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:45:56 -0400 +Subject: smb: client: require a full NFS mode SID before reading mode bits +To: stable@vger.kernel.org +Cc: Michael Bommarito , Steve French , Sasha Levin +Message-ID: <20260424144556.2184965-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit 2757ad3e4b6f9e0fed4c7739594e702abc5cab21 ] + +parse_dacl() treats an ACE SID matching sid_unix_NFS_mode as an NFS +mode SID and reads sid.sub_auth[2] to recover the mode bits. + +That assumes the ACE carries three subauthorities, but compare_sids() +only compares min(a, b) subauthorities. A malicious server can return +an ACE with num_subauth = 2 and sub_auth[] = {88, 3}, which still +matches sid_unix_NFS_mode and then drives the sub_auth[2] read four +bytes past the end of the ACE. + +Require num_subauth >= 3 before treating the ACE as an NFS mode SID. +This keeps the fix local to the special-SID mode path without changing +compare_sids() semantics for the rest of cifsacl. + +Fixes: e2f8fbfb8d09 ("cifs: get mode bits from special sid on stat") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-6 +Signed-off-by: Michael Bommarito +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/cifsacl.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/cifs/cifsacl.c ++++ b/fs/cifs/cifsacl.c +@@ -757,6 +757,7 @@ static void parse_dacl(struct cifs_acl * + dump_ace(ppace[i], end_of_acl); + #endif + if (mode_from_special_sid && ++ ppace[i]->sid.num_subauth >= 3 && + (compare_sids(&(ppace[i]->sid), + &sid_unix_NFS_mode) == 0)) { + /* diff --git a/queue-5.10/smb-client-use-fullsessionkey-for-aes-256-encryption-key-derivation.patch b/queue-5.10/smb-client-use-fullsessionkey-for-aes-256-encryption-key-derivation.patch new file mode 100644 index 0000000000..829f301c2f --- /dev/null +++ b/queue-5.10/smb-client-use-fullsessionkey-for-aes-256-encryption-key-derivation.patch @@ -0,0 +1,146 @@ +From stable+bounces-249163-greg=kroah.com@vger.kernel.org Mon May 18 06:54:12 2026 +From: Sasha Levin +Date: Sun, 17 May 2026 21:24:03 -0400 +Subject: smb: client: Use FullSessionKey for AES-256 encryption key derivation +To: stable@vger.kernel.org +Cc: Piyush Sachdeva , Bharath SM , Piyush Sachdeva , Steve French , Sasha Levin +Message-ID: <20260518012403.482990-1-sashal@kernel.org> + +From: Piyush Sachdeva + +[ Upstream commit 5be7a0cef3229fb3b63a07c0d289daf752545424 ] + +When Kerberos authentication is used with AES-256 encryption (AES-256-CCM +or AES-256-GCM), the SMB3 encryption and decryption keys must be derived +using the full session key (Session.FullSessionKey) rather than just the +first 16 bytes (Session.SessionKey). + +Per MS-SMB2 section 3.2.5.3.1, when Connection.Dialect is "3.1.1" and +Connection.CipherId is AES-256-CCM or AES-256-GCM, Session.FullSessionKey +must be set to the full cryptographic key from the GSS authentication +context. The encryption and decryption key derivation (SMBC2SCipherKey, +SMBS2CCipherKey) must use this FullSessionKey as the KDF input. The +signing key derivation continues to use Session.SessionKey (first 16 +bytes) in all cases. + +Previously, generate_key() hardcoded SMB2_NTLMV2_SESSKEY_SIZE (16) as the +HMAC-SHA256 key input length for all derivations. When Kerberos with +AES-256 provides a 32-byte session key, the KDF for encryption/decryption +was using only the first 16 bytes, producing keys that did not match the +server's, causing mount failures with sec=krb5 and require_gcm_256=1. + +Add a full_key_size parameter to generate_key() and pass the appropriate +size from generate_smb3signingkey(): + - Signing: always SMB2_NTLMV2_SESSKEY_SIZE (16 bytes) + - Encryption/Decryption: ses->auth_key.len when AES-256, otherwise 16 + +Also fix cifs_dump_full_key() to report the actual session key length for +AES-256 instead of hardcoded CIFS_SESS_KEY_SIZE, so that userspace tools +like Wireshark receive the correct key for decryption. + +Cc: +Reviewed-by: Bharath SM +Signed-off-by: Piyush Sachdeva +Signed-off-by: Piyush Sachdeva +Signed-off-by: Steve French +[ adapted to old crypto_shash API ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/cifs/smb2transport.c | 36 ++++++++++++++++++++++++++---------- + 1 file changed, 26 insertions(+), 10 deletions(-) + +--- a/fs/cifs/smb2transport.c ++++ b/fs/cifs/smb2transport.c +@@ -305,7 +305,8 @@ out: + } + + static int generate_key(struct cifs_ses *ses, struct kvec label, +- struct kvec context, __u8 *key, unsigned int key_size) ++ struct kvec context, __u8 *key, unsigned int key_size, ++ unsigned int full_key_size) + { + unsigned char zero = 0x0; + __u8 i[4] = {0, 0, 0, 1}; +@@ -326,7 +327,7 @@ static int generate_key(struct cifs_ses + } + + rc = crypto_shash_setkey(server->secmech.hmacsha256, +- ses->auth_key.response, SMB2_NTLMV2_SESSKEY_SIZE); ++ ses->auth_key.response, full_key_size); + if (rc) { + cifs_server_dbg(VFS, "%s: Could not set with session key\n", __func__); + goto smb3signkey_ret; +@@ -407,10 +408,9 @@ static int + generate_smb3signingkey(struct cifs_ses *ses, + const struct derivation_triplet *ptriplet) + { +- int rc; +-#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS ++ unsigned int full_key_size = SMB2_NTLMV2_SESSKEY_SIZE; + struct TCP_Server_Info *server = ses->server; +-#endif ++ int rc; + + /* + * All channels use the same encryption/decryption keys but +@@ -426,30 +426,46 @@ generate_smb3signingkey(struct cifs_ses + rc = generate_key(ses, ptriplet->signing.label, + ptriplet->signing.context, + cifs_ses_binding_channel(ses)->signkey, +- SMB3_SIGN_KEY_SIZE); ++ SMB3_SIGN_KEY_SIZE, ++ SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + return rc; + } else { + rc = generate_key(ses, ptriplet->signing.label, + ptriplet->signing.context, + ses->smb3signingkey, +- SMB3_SIGN_KEY_SIZE); ++ SMB3_SIGN_KEY_SIZE, ++ SMB2_NTLMV2_SESSKEY_SIZE); + if (rc) + return rc; + ++ /* ++ * Per MS-SMB2 3.2.5.3.1, signing key always uses Session.SessionKey ++ * (first 16 bytes). Encryption/decryption keys use ++ * Session.FullSessionKey when dialect is 3.1.1 and cipher is ++ * AES-256-CCM or AES-256-GCM, otherwise Session.SessionKey. ++ */ ++ ++ if (server->dialect == SMB311_PROT_ID && ++ (server->cipher_type == SMB2_ENCRYPTION_AES256_CCM || ++ server->cipher_type == SMB2_ENCRYPTION_AES256_GCM)) ++ full_key_size = ses->auth_key.len; ++ + memcpy(ses->chans[0].signkey, ses->smb3signingkey, + SMB3_SIGN_KEY_SIZE); + + rc = generate_key(ses, ptriplet->encryption.label, + ptriplet->encryption.context, + ses->smb3encryptionkey, +- SMB3_ENC_DEC_KEY_SIZE); ++ SMB3_ENC_DEC_KEY_SIZE, ++ full_key_size); + if (rc) + return rc; + rc = generate_key(ses, ptriplet->decryption.label, + ptriplet->decryption.context, + ses->smb3decryptionkey, +- SMB3_ENC_DEC_KEY_SIZE); ++ SMB3_ENC_DEC_KEY_SIZE, ++ full_key_size); + if (rc) + return rc; + } +@@ -464,7 +480,7 @@ generate_smb3signingkey(struct cifs_ses + &ses->Suid); + cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type); + cifs_dbg(VFS, "Session Key %*ph\n", +- SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response); ++ (int)ses->auth_key.len, ses->auth_key.response); + cifs_dbg(VFS, "Signing Key %*ph\n", + SMB3_SIGN_KEY_SIZE, ses->smb3signingkey); + if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) || diff --git a/queue-5.10/spi-lantiq-ssc-fix-controller-deregistration.patch b/queue-5.10/spi-lantiq-ssc-fix-controller-deregistration.patch new file mode 100644 index 0000000000..1d7ec3f7f8 --- /dev/null +++ b/queue-5.10/spi-lantiq-ssc-fix-controller-deregistration.patch @@ -0,0 +1,59 @@ +From stable+bounces-250018-greg=kroah.com@vger.kernel.org Wed May 20 21:35:10 2026 +From: Sasha Levin +Date: Wed, 20 May 2026 11:24:08 -0400 +Subject: spi: lantiq-ssc: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Hauke Mehrtens , Mark Brown , Sasha Levin +Message-ID: <20260520152408.3943826-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit b99206710d032c16b7f8b75e4bc18414d8e4b9f4 ] + +Make sure to deregister the controller before releasing underlying +resources like clocks during driver unbind. + +Fixes: 17f84b793c01 ("spi: lantiq-ssc: add support for Lantiq SSC SPI controller") +Cc: stable@vger.kernel.org # 4.11 +Cc: Hauke Mehrtens +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260409120419.388546-17-johan@kernel.org +Signed-off-by: Mark Brown +[ adapted spi_controller/host naming to spi_master/master and preserved the int-returning remove() with trailing return 0 ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-lantiq-ssc.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-lantiq-ssc.c ++++ b/drivers/spi/spi-lantiq-ssc.c +@@ -1003,7 +1003,7 @@ static int lantiq_ssc_probe(struct platf + "Lantiq SSC SPI controller (Rev %i, TXFS %u, RXFS %u, DMA %u)\n", + revision, spi->tx_fifo_size, spi->rx_fifo_size, supports_dma); + +- err = devm_spi_register_master(dev, master); ++ err = spi_register_master(master); + if (err) { + dev_err(dev, "failed to register spi_master\n"); + goto err_wq_destroy; +@@ -1027,6 +1027,10 @@ static int lantiq_ssc_remove(struct plat + { + struct lantiq_ssc_spi *spi = platform_get_drvdata(pdev); + ++ spi_master_get(spi->master); ++ ++ spi_unregister_master(spi->master); ++ + lantiq_ssc_writel(spi, 0, LTQ_SPI_IRNEN); + lantiq_ssc_writel(spi, 0, LTQ_SPI_CLC); + rx_fifo_flush(spi); +@@ -1037,6 +1041,8 @@ static int lantiq_ssc_remove(struct plat + clk_disable_unprepare(spi->spi_clk); + clk_put(spi->fpi_clk); + ++ spi_master_put(spi->master); ++ + return 0; + } + diff --git a/queue-5.10/spi-qup-fix-error-pointer-deref-after-dma-setup-failure.patch b/queue-5.10/spi-qup-fix-error-pointer-deref-after-dma-setup-failure.patch new file mode 100644 index 0000000000..475d1c4196 --- /dev/null +++ b/queue-5.10/spi-qup-fix-error-pointer-deref-after-dma-setup-failure.patch @@ -0,0 +1,47 @@ +From stable+bounces-259521-greg=kroah.com@vger.kernel.org Mon Jun 1 16:21:18 2026 +From: Sasha Levin +Date: Mon, 1 Jun 2026 06:51:08 -0400 +Subject: spi: qup: fix error pointer deref after DMA setup failure +To: stable@vger.kernel.org +Cc: Johan Hovold , Mark Brown , Sasha Levin +Message-ID: <20260601105108.378152-2-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit a7e8f3efd50a165ba0189f6dc57f7e51a7d149db ] + +The driver falls back to PIO mode if DMA setup fails during probe. + +Make sure to the clear the DMA channel pointers on setup failure to +avoid dereferencing an error pointer (or attempting to release a channel +a second time) on later probe errors or driver unbind. + +This issue was flagged by Sashiko when reviewing a devres allocation +conversion patch. + +Fixes: 612762e82ae6 ("spi: qup: Add DMA capabilities") +Link: https://sashiko.dev/#/patchset/20260505072909.618363-1-johan%40kernel.org?part=4 +Cc: stable@vger.kernel.org # 4.1 +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260512074334.914735-1-johan@kernel.org +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-qup.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -969,8 +969,11 @@ static int spi_qup_init_dma(struct spi_c + + err: + dma_release_channel(host->dma_tx); ++ host->dma_tx = NULL; + err_tx: + dma_release_channel(host->dma_rx); ++ host->dma_rx = NULL; ++ + return ret; + } + diff --git a/queue-5.10/spi-qup-switch-to-use-modern-name.patch b/queue-5.10/spi-qup-switch-to-use-modern-name.patch new file mode 100644 index 0000000000..533e8cf105 --- /dev/null +++ b/queue-5.10/spi-qup-switch-to-use-modern-name.patch @@ -0,0 +1,459 @@ +From stable+bounces-259520-greg=kroah.com@vger.kernel.org Mon Jun 1 16:21:16 2026 +From: Sasha Levin +Date: Mon, 1 Jun 2026 06:51:07 -0400 +Subject: spi: qup: switch to use modern name +To: stable@vger.kernel.org +Cc: Yang Yingliang , Mark Brown , Sasha Levin +Message-ID: <20260601105108.378152-1-sashal@kernel.org> + +From: Yang Yingliang + +[ Upstream commit 597442ff4f6226206b7cc28b86eb2be0ae9c6418 ] + +Change legacy name master to modern name host or controller. + +No functional changed. + +Signed-off-by: Yang Yingliang +Link: https://lore.kernel.org/r/20230818093154.1183529-10-yangyingliang@huawei.com +Signed-off-by: Mark Brown +Stable-dep-of: a7e8f3efd50a ("spi: qup: fix error pointer deref after DMA setup failure") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-qup.c | 164 +++++++++++++++++++++++++------------------------- + 1 file changed, 82 insertions(+), 82 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -386,20 +386,20 @@ static void spi_qup_write(struct spi_qup + } while (remainder); + } + +-static int spi_qup_prep_sg(struct spi_master *master, struct scatterlist *sgl, ++static int spi_qup_prep_sg(struct spi_controller *host, struct scatterlist *sgl, + unsigned int nents, enum dma_transfer_direction dir, + dma_async_tx_callback callback) + { +- struct spi_qup *qup = spi_master_get_devdata(master); ++ struct spi_qup *qup = spi_controller_get_devdata(host); + unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE; + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan; + dma_cookie_t cookie; + + if (dir == DMA_MEM_TO_DEV) +- chan = master->dma_tx; ++ chan = host->dma_tx; + else +- chan = master->dma_rx; ++ chan = host->dma_rx; + + desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags); + if (IS_ERR_OR_NULL(desc)) +@@ -413,13 +413,13 @@ static int spi_qup_prep_sg(struct spi_ma + return dma_submit_error(cookie); + } + +-static void spi_qup_dma_terminate(struct spi_master *master, ++static void spi_qup_dma_terminate(struct spi_controller *host, + struct spi_transfer *xfer) + { + if (xfer->tx_buf) +- dmaengine_terminate_all(master->dma_tx); ++ dmaengine_terminate_all(host->dma_tx); + if (xfer->rx_buf) +- dmaengine_terminate_all(master->dma_rx); ++ dmaengine_terminate_all(host->dma_rx); + } + + static u32 spi_qup_sgl_get_nents_len(struct scatterlist *sgl, u32 max, +@@ -446,8 +446,8 @@ static int spi_qup_do_dma(struct spi_dev + unsigned long timeout) + { + dma_async_tx_callback rx_done = NULL, tx_done = NULL; +- struct spi_master *master = spi->master; +- struct spi_qup *qup = spi_master_get_devdata(master); ++ struct spi_controller *host = spi->controller; ++ struct spi_qup *qup = spi_controller_get_devdata(host); + struct scatterlist *tx_sgl, *rx_sgl; + int ret; + +@@ -482,20 +482,20 @@ static int spi_qup_do_dma(struct spi_dev + return ret; + } + if (rx_sgl) { +- ret = spi_qup_prep_sg(master, rx_sgl, rx_nents, ++ ret = spi_qup_prep_sg(host, rx_sgl, rx_nents, + DMA_DEV_TO_MEM, rx_done); + if (ret) + return ret; +- dma_async_issue_pending(master->dma_rx); ++ dma_async_issue_pending(host->dma_rx); + } + + if (tx_sgl) { +- ret = spi_qup_prep_sg(master, tx_sgl, tx_nents, ++ ret = spi_qup_prep_sg(host, tx_sgl, tx_nents, + DMA_MEM_TO_DEV, tx_done); + if (ret) + return ret; + +- dma_async_issue_pending(master->dma_tx); ++ dma_async_issue_pending(host->dma_tx); + } + + if (!wait_for_completion_timeout(&qup->done, timeout)) +@@ -514,8 +514,8 @@ static int spi_qup_do_dma(struct spi_dev + static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer, + unsigned long timeout) + { +- struct spi_master *master = spi->master; +- struct spi_qup *qup = spi_master_get_devdata(master); ++ struct spi_controller *host = spi->controller; ++ struct spi_qup *qup = spi_controller_get_devdata(host); + int ret, n_words, iterations, offset = 0; + + n_words = qup->n_words; +@@ -660,7 +660,7 @@ static irqreturn_t spi_qup_qup_irq(int i + /* set clock freq ... bits per word, determine mode */ + static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer) + { +- struct spi_qup *controller = spi_master_get_devdata(spi->master); ++ struct spi_qup *controller = spi_controller_get_devdata(spi->controller); + int ret; + + if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { +@@ -681,9 +681,9 @@ static int spi_qup_io_prep(struct spi_de + + if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32))) + controller->mode = QUP_IO_M_MODE_FIFO; +- else if (spi->master->can_dma && +- spi->master->can_dma(spi->master, spi, xfer) && +- spi->master->cur_msg_mapped) ++ else if (spi->controller->can_dma && ++ spi->controller->can_dma(spi->controller, spi, xfer) && ++ spi->controller->cur_msg_mapped) + controller->mode = QUP_IO_M_MODE_BAM; + else + controller->mode = QUP_IO_M_MODE_BLOCK; +@@ -694,7 +694,7 @@ static int spi_qup_io_prep(struct spi_de + /* prep qup for another spi transaction of specific type */ + static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) + { +- struct spi_qup *controller = spi_master_get_devdata(spi->master); ++ struct spi_qup *controller = spi_controller_get_devdata(spi->controller); + u32 config, iomode, control; + unsigned long flags; + +@@ -842,11 +842,11 @@ static int spi_qup_io_config(struct spi_ + return 0; + } + +-static int spi_qup_transfer_one(struct spi_master *master, ++static int spi_qup_transfer_one(struct spi_controller *host, + struct spi_device *spi, + struct spi_transfer *xfer) + { +- struct spi_qup *controller = spi_master_get_devdata(master); ++ struct spi_qup *controller = spi_controller_get_devdata(host); + unsigned long timeout, flags; + int ret; + +@@ -880,21 +880,21 @@ static int spi_qup_transfer_one(struct s + spin_unlock_irqrestore(&controller->lock, flags); + + if (ret && spi_qup_is_dma_xfer(controller->mode)) +- spi_qup_dma_terminate(master, xfer); ++ spi_qup_dma_terminate(host, xfer); + + return ret; + } + +-static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi, ++static bool spi_qup_can_dma(struct spi_controller *host, struct spi_device *spi, + struct spi_transfer *xfer) + { +- struct spi_qup *qup = spi_master_get_devdata(master); ++ struct spi_qup *qup = spi_controller_get_devdata(host); + size_t dma_align = dma_get_cache_alignment(); + int n_words; + + if (xfer->rx_buf) { + if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) || +- IS_ERR_OR_NULL(master->dma_rx)) ++ IS_ERR_OR_NULL(host->dma_rx)) + return false; + if (qup->qup_v1 && (xfer->len % qup->in_blk_sz)) + return false; +@@ -902,7 +902,7 @@ static bool spi_qup_can_dma(struct spi_m + + if (xfer->tx_buf) { + if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) || +- IS_ERR_OR_NULL(master->dma_tx)) ++ IS_ERR_OR_NULL(host->dma_tx)) + return false; + if (qup->qup_v1 && (xfer->len % qup->out_blk_sz)) + return false; +@@ -915,30 +915,30 @@ static bool spi_qup_can_dma(struct spi_m + return true; + } + +-static void spi_qup_release_dma(struct spi_master *master) ++static void spi_qup_release_dma(struct spi_controller *host) + { +- if (!IS_ERR_OR_NULL(master->dma_rx)) +- dma_release_channel(master->dma_rx); +- if (!IS_ERR_OR_NULL(master->dma_tx)) +- dma_release_channel(master->dma_tx); ++ if (!IS_ERR_OR_NULL(host->dma_rx)) ++ dma_release_channel(host->dma_rx); ++ if (!IS_ERR_OR_NULL(host->dma_tx)) ++ dma_release_channel(host->dma_tx); + } + +-static int spi_qup_init_dma(struct spi_master *master, resource_size_t base) ++static int spi_qup_init_dma(struct spi_controller *host, resource_size_t base) + { +- struct spi_qup *spi = spi_master_get_devdata(master); ++ struct spi_qup *spi = spi_controller_get_devdata(host); + struct dma_slave_config *rx_conf = &spi->rx_conf, + *tx_conf = &spi->tx_conf; + struct device *dev = spi->dev; + int ret; + + /* allocate dma resources, if available */ +- master->dma_rx = dma_request_chan(dev, "rx"); +- if (IS_ERR(master->dma_rx)) +- return PTR_ERR(master->dma_rx); +- +- master->dma_tx = dma_request_chan(dev, "tx"); +- if (IS_ERR(master->dma_tx)) { +- ret = PTR_ERR(master->dma_tx); ++ host->dma_rx = dma_request_chan(dev, "rx"); ++ if (IS_ERR(host->dma_rx)) ++ return PTR_ERR(host->dma_rx); ++ ++ host->dma_tx = dma_request_chan(dev, "tx"); ++ if (IS_ERR(host->dma_tx)) { ++ ret = PTR_ERR(host->dma_tx); + goto err_tx; + } + +@@ -953,13 +953,13 @@ static int spi_qup_init_dma(struct spi_m + tx_conf->dst_addr = base + QUP_OUTPUT_FIFO; + tx_conf->dst_maxburst = spi->out_blk_sz; + +- ret = dmaengine_slave_config(master->dma_rx, rx_conf); ++ ret = dmaengine_slave_config(host->dma_rx, rx_conf); + if (ret) { + dev_err(dev, "failed to configure RX channel\n"); + goto err; + } + +- ret = dmaengine_slave_config(master->dma_tx, tx_conf); ++ ret = dmaengine_slave_config(host->dma_tx, tx_conf); + if (ret) { + dev_err(dev, "failed to configure TX channel\n"); + goto err; +@@ -968,9 +968,9 @@ static int spi_qup_init_dma(struct spi_m + return 0; + + err: +- dma_release_channel(master->dma_tx); ++ dma_release_channel(host->dma_tx); + err_tx: +- dma_release_channel(master->dma_rx); ++ dma_release_channel(host->dma_rx); + return ret; + } + +@@ -980,7 +980,7 @@ static void spi_qup_set_cs(struct spi_de + u32 spi_ioc; + u32 spi_ioc_orig; + +- controller = spi_master_get_devdata(spi->master); ++ controller = spi_controller_get_devdata(spi->controller); + spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL); + spi_ioc_orig = spi_ioc; + if (!val) +@@ -994,7 +994,7 @@ static void spi_qup_set_cs(struct spi_de + + static int spi_qup_probe(struct platform_device *pdev) + { +- struct spi_master *master; ++ struct spi_controller *host; + struct clk *iclk, *cclk; + struct spi_qup *controller; + struct resource *res; +@@ -1030,32 +1030,32 @@ static int spi_qup_probe(struct platform + return -ENXIO; + } + +- master = spi_alloc_master(dev, sizeof(struct spi_qup)); +- if (!master) { +- dev_err(dev, "cannot allocate master\n"); ++ host = spi_alloc_master(dev, sizeof(struct spi_qup)); ++ if (!host) { ++ dev_err(dev, "cannot allocate host\n"); + return -ENOMEM; + } + + /* use num-cs unless not present or out of range */ + if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) || + num_cs > SPI_NUM_CHIPSELECTS) +- master->num_chipselect = SPI_NUM_CHIPSELECTS; ++ host->num_chipselect = SPI_NUM_CHIPSELECTS; + else +- master->num_chipselect = num_cs; ++ host->num_chipselect = num_cs; + +- master->bus_num = pdev->id; +- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; +- master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); +- master->max_speed_hz = max_freq; +- master->transfer_one = spi_qup_transfer_one; +- master->dev.of_node = pdev->dev.of_node; +- master->auto_runtime_pm = true; +- master->dma_alignment = dma_get_cache_alignment(); +- master->max_dma_len = SPI_MAX_XFER; ++ host->bus_num = pdev->id; ++ host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; ++ host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); ++ host->max_speed_hz = max_freq; ++ host->transfer_one = spi_qup_transfer_one; ++ host->dev.of_node = pdev->dev.of_node; ++ host->auto_runtime_pm = true; ++ host->dma_alignment = dma_get_cache_alignment(); ++ host->max_dma_len = SPI_MAX_XFER; + +- platform_set_drvdata(pdev, master); ++ platform_set_drvdata(pdev, host); + +- controller = spi_master_get_devdata(master); ++ controller = spi_controller_get_devdata(host); + + controller->dev = dev; + controller->base = base; +@@ -1063,16 +1063,16 @@ static int spi_qup_probe(struct platform + controller->cclk = cclk; + controller->irq = irq; + +- ret = spi_qup_init_dma(master, res->start); ++ ret = spi_qup_init_dma(host, res->start); + if (ret == -EPROBE_DEFER) + goto error; + else if (!ret) +- master->can_dma = spi_qup_can_dma; ++ host->can_dma = spi_qup_can_dma; + + controller->qup_v1 = (uintptr_t)of_device_get_match_data(dev); + + if (!controller->qup_v1) +- master->set_cs = spi_qup_set_cs; ++ host->set_cs = spi_qup_set_cs; + + spin_lock_init(&controller->lock); + init_completion(&controller->done); +@@ -1150,7 +1150,7 @@ static int spi_qup_probe(struct platform + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + +- ret = devm_spi_register_master(dev, master); ++ ret = devm_spi_register_controller(dev, host); + if (ret) + goto disable_pm; + +@@ -1162,17 +1162,17 @@ error_clk: + clk_disable_unprepare(cclk); + clk_disable_unprepare(iclk); + error_dma: +- spi_qup_release_dma(master); ++ spi_qup_release_dma(host); + error: +- spi_master_put(master); ++ spi_controller_put(host); + return ret; + } + + #ifdef CONFIG_PM + static int spi_qup_pm_suspend_runtime(struct device *device) + { +- struct spi_master *master = dev_get_drvdata(device); +- struct spi_qup *controller = spi_master_get_devdata(master); ++ struct spi_controller *host = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_controller_get_devdata(host); + u32 config; + + /* Enable clocks auto gaiting */ +@@ -1188,8 +1188,8 @@ static int spi_qup_pm_suspend_runtime(st + + static int spi_qup_pm_resume_runtime(struct device *device) + { +- struct spi_master *master = dev_get_drvdata(device); +- struct spi_qup *controller = spi_master_get_devdata(master); ++ struct spi_controller *host = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_controller_get_devdata(host); + u32 config; + int ret; + +@@ -1214,8 +1214,8 @@ static int spi_qup_pm_resume_runtime(str + #ifdef CONFIG_PM_SLEEP + static int spi_qup_suspend(struct device *device) + { +- struct spi_master *master = dev_get_drvdata(device); +- struct spi_qup *controller = spi_master_get_devdata(master); ++ struct spi_controller *host = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_controller_get_devdata(host); + int ret; + + if (pm_runtime_suspended(device)) { +@@ -1223,7 +1223,7 @@ static int spi_qup_suspend(struct device + if (ret) + return ret; + } +- ret = spi_master_suspend(master); ++ ret = spi_controller_suspend(host); + if (ret) + return ret; + +@@ -1238,8 +1238,8 @@ static int spi_qup_suspend(struct device + + static int spi_qup_resume(struct device *device) + { +- struct spi_master *master = dev_get_drvdata(device); +- struct spi_qup *controller = spi_master_get_devdata(master); ++ struct spi_controller *host = dev_get_drvdata(device); ++ struct spi_qup *controller = spi_controller_get_devdata(host); + int ret; + + ret = clk_prepare_enable(controller->iclk); +@@ -1256,7 +1256,7 @@ static int spi_qup_resume(struct device + if (ret) + goto disable_clk; + +- ret = spi_master_resume(master); ++ ret = spi_controller_resume(host); + if (ret) + goto disable_clk; + +@@ -1271,8 +1271,8 @@ disable_clk: + + static int spi_qup_remove(struct platform_device *pdev) + { +- struct spi_master *master = dev_get_drvdata(&pdev->dev); +- struct spi_qup *controller = spi_master_get_devdata(master); ++ struct spi_controller *host = dev_get_drvdata(&pdev->dev); ++ struct spi_qup *controller = spi_controller_get_devdata(host); + int ret; + + ret = pm_runtime_get_sync(&pdev->dev); +@@ -1290,7 +1290,7 @@ static int spi_qup_remove(struct platfor + ERR_PTR(ret)); + } + +- spi_qup_release_dma(master); ++ spi_qup_release_dma(host); + + pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); diff --git a/queue-5.10/spi-st-ssc4-fix-controller-deregistration.patch b/queue-5.10/spi-st-ssc4-fix-controller-deregistration.patch new file mode 100644 index 0000000000..c53dfb0c33 --- /dev/null +++ b/queue-5.10/spi-st-ssc4-fix-controller-deregistration.patch @@ -0,0 +1,56 @@ +From stable+bounces-250013-greg=kroah.com@vger.kernel.org Wed May 20 20:54:32 2026 +From: Sasha Levin +Date: Wed, 20 May 2026 11:18:24 -0400 +Subject: spi: st-ssc4: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Lee Jones , Mark Brown , Sasha Levin +Message-ID: <20260520151824.3913740-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 19857374010d06ca6a2f7c2c53464122eb804df0 ] + +Make sure to deregister the controller before disabling underlying +resources like clocks during driver unbind. + +Fixes: 9e862375c542 ("spi: Add new driver for STMicroelectronics' SPI Controller") +Cc: stable@vger.kernel.org # 4.0 +Cc: Lee Jones +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-18-johan@kernel.org +Signed-off-by: Mark Brown +[ changed spi_controller/host API calls to spi_master/master equivalents ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-st-ssc4.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-st-ssc4.c ++++ b/drivers/spi/spi-st-ssc4.c +@@ -372,7 +372,7 @@ static int spi_st_probe(struct platform_ + + platform_set_drvdata(pdev, master); + +- ret = devm_spi_register_master(&pdev->dev, master); ++ ret = spi_register_master(master); + if (ret) { + dev_err(&pdev->dev, "Failed to register master\n"); + goto rpm_disable; +@@ -394,10 +394,16 @@ static int spi_st_remove(struct platform + struct spi_master *master = platform_get_drvdata(pdev); + struct spi_st *spi_st = spi_master_get_devdata(master); + ++ spi_master_get(master); ++ ++ spi_unregister_master(master); ++ + pm_runtime_disable(&pdev->dev); + + clk_disable_unprepare(spi_st->clk); + ++ spi_master_put(master); ++ + pinctrl_pm_select_sleep_state(&pdev->dev); + + return 0; diff --git a/queue-5.10/spi-sun4i-fix-controller-deregistration.patch b/queue-5.10/spi-sun4i-fix-controller-deregistration.patch new file mode 100644 index 0000000000..bb22eb31f9 --- /dev/null +++ b/queue-5.10/spi-sun4i-fix-controller-deregistration.patch @@ -0,0 +1,56 @@ +From stable+bounces-247017-greg=kroah.com@vger.kernel.org Thu May 14 00:10:21 2026 +From: Sasha Levin +Date: Wed, 13 May 2026 14:34:01 -0400 +Subject: spi: sun4i: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Maxime Ripard , Mark Brown , Sasha Levin +Message-ID: <20260513183401.3927454-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 42108a2f03e0fdeabe9d02d085bdb058baa1189f ] + +Make sure to deregister the controller before disabling underlying +resources like clocks during driver unbind. + +Fixes: b5f6517948cc ("spi: sunxi: Add Allwinner A10 SPI controller driver") +Cc: stable@vger.kernel.org # 3.15 +Cc: Maxime Ripard +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-19-johan@kernel.org +Signed-off-by: Mark Brown +[ renamed `host`/`spi_controller` to `master`/`spi_master` and kept `int` return type with `return 0` in remove ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-sun4i.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-sun4i.c ++++ b/drivers/spi/spi-sun4i.c +@@ -503,7 +503,7 @@ static int sun4i_spi_probe(struct platfo + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + +- ret = devm_spi_register_master(&pdev->dev, master); ++ ret = spi_register_master(master); + if (ret) { + dev_err(&pdev->dev, "cannot register SPI master\n"); + goto err_pm_disable; +@@ -521,8 +521,16 @@ err_free_master: + + static int sun4i_spi_remove(struct platform_device *pdev) + { ++ struct spi_master *master = platform_get_drvdata(pdev); ++ ++ spi_master_get(master); ++ ++ spi_unregister_master(master); ++ + pm_runtime_force_suspend(&pdev->dev); + ++ spi_master_put(master); ++ + return 0; + } + diff --git a/queue-5.10/spi-sun6i-fix-controller-deregistration.patch b/queue-5.10/spi-sun6i-fix-controller-deregistration.patch new file mode 100644 index 0000000000..e744e2cad4 --- /dev/null +++ b/queue-5.10/spi-sun6i-fix-controller-deregistration.patch @@ -0,0 +1,56 @@ +From stable+bounces-247111-greg=kroah.com@vger.kernel.org Thu May 14 10:28:53 2026 +From: Sasha Levin +Date: Thu, 14 May 2026 00:58:46 -0400 +Subject: spi: sun6i: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Maxime Ripard , Mark Brown , Sasha Levin +Message-ID: <20260514045846.24524-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit d874a1c33aee0d88fb4ba2f8aeadaa9f1965209a ] + +Make sure to deregister the controller before disabling underlying +resources like clocks during driver unbind. + +Fixes: 3558fe900e8a ("spi: sunxi: Add Allwinner A31 SPI controller driver") +Cc: stable@vger.kernel.org # 3.15 +Cc: Maxime Ripard +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-20-johan@kernel.org +Signed-off-by: Mark Brown +[ renamed spi_controller/host APIs to spi_master APIs, dropped non-existent DMA cleanup, and kept int return type ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-sun6i.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-sun6i.c ++++ b/drivers/spi/spi-sun6i.c +@@ -512,7 +512,7 @@ static int sun6i_spi_probe(struct platfo + pm_runtime_enable(&pdev->dev); + pm_runtime_idle(&pdev->dev); + +- ret = devm_spi_register_master(&pdev->dev, master); ++ ret = spi_register_master(master); + if (ret) { + dev_err(&pdev->dev, "cannot register SPI master\n"); + goto err_pm_disable; +@@ -530,8 +530,16 @@ err_free_master: + + static int sun6i_spi_remove(struct platform_device *pdev) + { ++ struct spi_master *master = platform_get_drvdata(pdev); ++ ++ spi_master_get(master); ++ ++ spi_unregister_master(master); ++ + pm_runtime_force_suspend(&pdev->dev); + ++ spi_master_put(master); ++ + return 0; + } + diff --git a/queue-5.10/spi-syncuacer-fix-controller-deregistration.patch b/queue-5.10/spi-syncuacer-fix-controller-deregistration.patch new file mode 100644 index 0000000000..70668c24da --- /dev/null +++ b/queue-5.10/spi-syncuacer-fix-controller-deregistration.patch @@ -0,0 +1,56 @@ +From stable+bounces-247016-greg=kroah.com@vger.kernel.org Thu May 14 00:10:05 2026 +From: Sasha Levin +Date: Wed, 13 May 2026 14:33:51 -0400 +Subject: spi: syncuacer: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Masahisa Kojima , Mark Brown , Sasha Levin +Message-ID: <20260513183351.3927329-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 75d849c3452e9611de031db45b3149ba9a99035f ] + +Make sure to deregister the controller before disabling underlying +resources like clocks during driver unbind. + +Fixes: b0823ee35cf9 ("spi: Add spi driver for Socionext SynQuacer platform") +Cc: stable@vger.kernel.org # 5.3 +Cc: Masahisa Kojima +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-21-johan@kernel.org +Signed-off-by: Mark Brown +[ renamed spi_controller/host to spi_master/master and kept int return type with `return 0;` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-synquacer.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-synquacer.c ++++ b/drivers/spi/spi-synquacer.c +@@ -719,7 +719,7 @@ static int synquacer_spi_probe(struct pl + pm_runtime_set_active(sspi->dev); + pm_runtime_enable(sspi->dev); + +- ret = devm_spi_register_master(sspi->dev, master); ++ ret = spi_register_master(master); + if (ret) + goto disable_pm; + +@@ -740,10 +740,16 @@ static int synquacer_spi_remove(struct p + struct spi_master *master = platform_get_drvdata(pdev); + struct synquacer_spi *sspi = spi_master_get_devdata(master); + ++ spi_master_get(master); ++ ++ spi_unregister_master(master); ++ + pm_runtime_disable(sspi->dev); + + clk_disable_unprepare(sspi->clk); + ++ spi_master_put(master); ++ + return 0; + } + diff --git a/queue-5.10/spi-tegra114-fix-controller-deregistration.patch b/queue-5.10/spi-tegra114-fix-controller-deregistration.patch new file mode 100644 index 0000000000..ca5512e2f1 --- /dev/null +++ b/queue-5.10/spi-tegra114-fix-controller-deregistration.patch @@ -0,0 +1,59 @@ +From stable+bounces-247193-greg=kroah.com@vger.kernel.org Thu May 14 18:10:39 2026 +From: Sasha Levin +Date: Thu, 14 May 2026 08:40:30 -0400 +Subject: spi: tegra114: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Jingoo Han , Mark Brown , Sasha Levin +Message-ID: <20260514124030.210588-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 9c9c27ff2058142d8f800de3186d6864184958de ] + +Make sure to deregister the controller before disabling underlying +resources like clocks during driver unbind. + +Fixes: 5c8096439600 ("spi: tegra114: use devm_spi_register_master()") +Cc: stable@vger.kernel.org # 3.13 +Cc: Jingoo Han +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-22-johan@kernel.org +Signed-off-by: Mark Brown +[ renamed spi_controller/host APIs to spi_master/master equivalents and placed spi_master_put() before the existing return 0 in remove ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-tegra114.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-tegra114.c ++++ b/drivers/spi/spi-tegra114.c +@@ -1422,7 +1422,7 @@ static int tegra_spi_probe(struct platfo + } + + master->dev.of_node = pdev->dev.of_node; +- ret = devm_spi_register_master(&pdev->dev, master); ++ ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "can not register to master err %d\n", ret); + goto exit_free_irq; +@@ -1448,6 +1448,10 @@ static int tegra_spi_remove(struct platf + struct spi_master *master = platform_get_drvdata(pdev); + struct tegra_spi_data *tspi = spi_master_get_devdata(master); + ++ spi_master_get(master); ++ ++ spi_unregister_master(master); ++ + free_irq(tspi->irq, tspi); + + if (tspi->tx_dma_chan) +@@ -1460,6 +1464,8 @@ static int tegra_spi_remove(struct platf + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_spi_runtime_suspend(&pdev->dev); + ++ spi_master_put(master); ++ + return 0; + } + diff --git a/queue-5.10/spi-tegra20-sflash-fix-controller-deregistration.patch b/queue-5.10/spi-tegra20-sflash-fix-controller-deregistration.patch new file mode 100644 index 0000000000..895d6afd96 --- /dev/null +++ b/queue-5.10/spi-tegra20-sflash-fix-controller-deregistration.patch @@ -0,0 +1,58 @@ +From stable+bounces-247192-greg=kroah.com@vger.kernel.org Thu May 14 18:12:25 2026 +From: Sasha Levin +Date: Thu, 14 May 2026 08:40:16 -0400 +Subject: spi: tegra20-sflash: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Jingoo Han , Mark Brown , Sasha Levin +Message-ID: <20260514124016.210394-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit ad7310e983327f939dd6c4e801eab13238992572 ] + +Make sure to deregister the controller before disabling underlying +resources like clocks during driver unbind. + +Fixes: f12f7318c44a ("spi: tegra20-sflash: use devm_spi_register_master()") +Cc: stable@vger.kernel.org # 3.13 +Cc: Jingoo Han +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-23-johan@kernel.org +Signed-off-by: Mark Brown +[ renamed spi_controller/host APIs to spi_master/master equivalents and switched devm_spi_register_master to spi_register_master ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-tegra20-sflash.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-tegra20-sflash.c ++++ b/drivers/spi/spi-tegra20-sflash.c +@@ -508,7 +508,7 @@ static int tegra_sflash_probe(struct pla + pm_runtime_put(&pdev->dev); + + master->dev.of_node = pdev->dev.of_node; +- ret = devm_spi_register_master(&pdev->dev, master); ++ ret = spi_register_master(master); + if (ret < 0) { + dev_err(&pdev->dev, "can not register to master err %d\n", ret); + goto exit_pm_disable; +@@ -531,12 +531,18 @@ static int tegra_sflash_remove(struct pl + struct spi_master *master = platform_get_drvdata(pdev); + struct tegra_sflash_data *tsd = spi_master_get_devdata(master); + ++ spi_master_get(master); ++ ++ spi_unregister_master(master); ++ + free_irq(tsd->irq, tsd); + + pm_runtime_disable(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + tegra_sflash_runtime_suspend(&pdev->dev); + ++ spi_master_put(master); ++ + return 0; + } + diff --git a/queue-5.10/spi-ti-qspi-fix-controller-deregistration.patch b/queue-5.10/spi-ti-qspi-fix-controller-deregistration.patch new file mode 100644 index 0000000000..177dd34a98 --- /dev/null +++ b/queue-5.10/spi-ti-qspi-fix-controller-deregistration.patch @@ -0,0 +1,68 @@ +From stable+bounces-247093-greg=kroah.com@vger.kernel.org Thu May 14 08:40:12 2026 +From: Sasha Levin +Date: Wed, 13 May 2026 23:10:05 -0400 +Subject: spi: ti-qspi: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Sebastian Andrzej Siewior , Mark Brown , Sasha Levin +Message-ID: <20260514031006.4117436-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 0c18a1bacbb1d8b8aa34d3d004a2cb8226c8b1ea ] + +Make sure to deregister the controller before disabling underlying +resources like clocks during driver unbind. + +Note that the controller is suspended before disabling and releasing +resources since commit 3ac066e2227c ("spi: spi-ti-qspi: Suspend the +queue before removing the device") which avoids issues like unclocked +accesses but prevents SPI device drivers from doing I/O during +deregistration. + +Fixes: 3b3a80019ff1 ("spi: ti-qspi: one only one interrupt handler") +Cc: stable@vger.kernel.org # 3.13 +Cc: Sebastian Andrzej Siewior +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-24-johan@kernel.org +Signed-off-by: Mark Brown +[ renamed spi_controller_*/host APIs to spi_master_*/master and kept remove() returning int ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-ti-qspi.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +--- a/drivers/spi/spi-ti-qspi.c ++++ b/drivers/spi/spi-ti-qspi.c +@@ -895,7 +895,7 @@ no_dma: + qspi->mmap_enabled = false; + qspi->current_cs = -1; + +- ret = devm_spi_register_master(&pdev->dev, master); ++ ret = spi_register_master(master); + if (!ret) + return 0; + +@@ -910,17 +910,18 @@ free_master: + static int ti_qspi_remove(struct platform_device *pdev) + { + struct ti_qspi *qspi = platform_get_drvdata(pdev); +- int rc; + +- rc = spi_master_suspend(qspi->master); +- if (rc) +- return rc; ++ spi_master_get(qspi->master); ++ ++ spi_unregister_master(qspi->master); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + ti_qspi_dma_cleanup(qspi); + ++ spi_master_put(qspi->master); ++ + return 0; + } + diff --git a/queue-5.10/spi-topcliff-pch-fix-controller-deregistration.patch b/queue-5.10/spi-topcliff-pch-fix-controller-deregistration.patch new file mode 100644 index 0000000000..ade02b5e64 --- /dev/null +++ b/queue-5.10/spi-topcliff-pch-fix-controller-deregistration.patch @@ -0,0 +1,48 @@ +From stable+bounces-247800-greg=kroah.com@vger.kernel.org Fri May 15 20:21:01 2026 +From: Sasha Levin +Date: Fri, 15 May 2026 10:48:54 -0400 +Subject: spi: topcliff-pch: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Masayuki Ohtake , Mark Brown , Sasha Levin +Message-ID: <20260515144854.3250320-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 5d6f477d6fc0767c57c5e1e6f55a1662820eef87 ] + +Make sure to deregister the controller before disabling and releasing +underlying resources like interrupts and DMA during driver unbind. + +Fixes: e8b17b5b3f30 ("spi/topcliff: Add topcliff platform controller hub (PCH) spi bus driver") +Cc: stable@vger.kernel.org # 2.6.37 +Cc: Masayuki Ohtake +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260414134319.978196-8-johan@kernel.org +Signed-off-by: Mark Brown +[ renamed data->host to data->master and kept return 0 ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-topcliff-pch.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-topcliff-pch.c ++++ b/drivers/spi/spi-topcliff-pch.c +@@ -1449,11 +1449,16 @@ static int pch_spi_pd_remove(struct plat + free_irq(board_dat->pdev->irq, data); + } + ++ spi_controller_get(data->master); ++ ++ spi_unregister_controller(data->master); ++ + if (use_dma) + pch_free_dma_buf(board_dat, data); + + pci_iounmap(board_dat->pdev, data->io_remap_addr); +- spi_unregister_master(data->master); ++ ++ spi_controller_put(data->master); + + return 0; + } diff --git a/queue-5.10/spi-uniphier-fix-controller-deregistration.patch b/queue-5.10/spi-uniphier-fix-controller-deregistration.patch new file mode 100644 index 0000000000..93c499da30 --- /dev/null +++ b/queue-5.10/spi-uniphier-fix-controller-deregistration.patch @@ -0,0 +1,63 @@ +From stable+bounces-247202-greg=kroah.com@vger.kernel.org Thu May 14 18:37:45 2026 +From: Sasha Levin +Date: Thu, 14 May 2026 09:06:38 -0400 +Subject: spi: uniphier: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Keiji Hayashibara , Mark Brown , Sasha Levin +Message-ID: <20260514130638.228220-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit 0245435f777264ac45945ed2f325dd095a41d1af ] + +Make sure to deregister the controller before releasing underlying +resources like DMA during driver unbind. + +Note that clocks were also disabled before the recent commit +fdca270f8f87 ("spi: uniphier: Simplify clock handling with +devm_clk_get_enabled()"). + +Fixes: 5ba155a4d4cc ("spi: add SPI controller driver for UniPhier SoC") +Cc: stable@vger.kernel.org # 4.19 +Cc: Keiji Hayashibara +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-25-johan@kernel.org +Signed-off-by: Mark Brown +[ renamed spi_*_controller/host APIs to spi_*_master/master aliases and kept the pre-existing clk_disable_unprepare() after unregister ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-uniphier.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-uniphier.c ++++ b/drivers/spi/spi-uniphier.c +@@ -751,7 +751,7 @@ static int uniphier_spi_probe(struct pla + + master->max_dma_len = min(dma_tx_burst, dma_rx_burst); + +- ret = devm_spi_register_master(&pdev->dev, master); ++ ret = spi_register_master(master); + if (ret) + goto out_release_dma; + +@@ -780,6 +780,10 @@ static int uniphier_spi_remove(struct pl + struct spi_master *master = platform_get_drvdata(pdev); + struct uniphier_spi_priv *priv = spi_master_get_devdata(master); + ++ spi_master_get(master); ++ ++ spi_unregister_master(master); ++ + if (master->dma_tx) + dma_release_channel(master->dma_tx); + if (master->dma_rx) +@@ -787,6 +791,8 @@ static int uniphier_spi_remove(struct pl + + clk_disable_unprepare(priv->clk); + ++ spi_master_put(master); ++ + return 0; + } + diff --git a/queue-5.10/spi-zynq-qspi-fix-controller-deregistration.patch b/queue-5.10/spi-zynq-qspi-fix-controller-deregistration.patch new file mode 100644 index 0000000000..84b2f12cec --- /dev/null +++ b/queue-5.10/spi-zynq-qspi-fix-controller-deregistration.patch @@ -0,0 +1,78 @@ +From stable+bounces-247098-greg=kroah.com@vger.kernel.org Thu May 14 09:44:27 2026 +From: Sasha Levin +Date: Thu, 14 May 2026 00:14:18 -0400 +Subject: spi: zynq-qspi: fix controller deregistration +To: stable@vger.kernel.org +Cc: Johan Hovold , Naga Sureshkumar Relli , Mark Brown , Sasha Levin +Message-ID: <20260514041418.4189826-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit c9c012706c9fa8ca6d129a9161caf92ab625a3fd ] + +Make sure to deregister the controller before disabling it during driver +unbind. + +Note that clocks were also disabled before the recent commit +1f8fd9490e31 ("spi: zynq-qspi: Simplify clock handling with +devm_clk_get_enabled()"). + +Fixes: 67dca5e580f1 ("spi: spi-mem: Add support for Zynq QSPI controller") +Cc: stable@vger.kernel.org # 5.2: 8eb2fd00f65a +Cc: stable@vger.kernel.org # 5.2 +Cc: Naga Sureshkumar Relli +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410081757.503099-27-johan@kernel.org +Signed-off-by: Mark Brown +[ kept int-returning remove() with manual clk_disable_unprepare() calls and routed probe error through existing clk_dis_all cascade instead of upstream's remove_ctlr label ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi-zynq-qspi.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/drivers/spi/spi-zynq-qspi.c ++++ b/drivers/spi/spi-zynq-qspi.c +@@ -652,7 +652,7 @@ static int zynq_qspi_probe(struct platfo + + xqspi = spi_controller_get_devdata(ctlr); + xqspi->dev = dev; +- platform_set_drvdata(pdev, xqspi); ++ platform_set_drvdata(pdev, ctlr); + xqspi->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xqspi->regs)) { + ret = PTR_ERR(xqspi->regs); +@@ -722,9 +722,9 @@ static int zynq_qspi_probe(struct platfo + /* QSPI controller initializations */ + zynq_qspi_init_hw(xqspi, ctlr->num_chipselect); + +- ret = devm_spi_register_controller(&pdev->dev, ctlr); ++ ret = spi_register_controller(ctlr); + if (ret) { +- dev_err(&pdev->dev, "spi_register_master failed\n"); ++ dev_err(&pdev->dev, "failed to register controller\n"); + goto clk_dis_all; + } + +@@ -752,13 +752,20 @@ remove_master: + */ + static int zynq_qspi_remove(struct platform_device *pdev) + { +- struct zynq_qspi *xqspi = platform_get_drvdata(pdev); ++ struct spi_controller *ctlr = platform_get_drvdata(pdev); ++ struct zynq_qspi *xqspi = spi_controller_get_devdata(ctlr); ++ ++ spi_controller_get(ctlr); ++ ++ spi_unregister_controller(ctlr); + + zynq_qspi_write(xqspi, ZYNQ_QSPI_ENABLE_OFFSET, 0); + + clk_disable_unprepare(xqspi->refclk); + clk_disable_unprepare(xqspi->pclk); + ++ spi_controller_put(ctlr); ++ + return 0; + } + diff --git a/queue-5.10/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch b/queue-5.10/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch new file mode 100644 index 0000000000..438c0b7434 --- /dev/null +++ b/queue-5.10/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch @@ -0,0 +1,69 @@ +From stable+bounces-242197-greg=kroah.com@vger.kernel.org Fri May 1 01:17:05 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 15:46:54 -0400 +Subject: thermal: core: Fix thermal zone governor cleanup issues +To: stable@vger.kernel.org +Cc: "Rafael J. Wysocki" , Sasha Levin +Message-ID: <20260430194654.1996047-1-sashal@kernel.org> + +From: "Rafael J. Wysocki" + +[ Upstream commit 41ff66baf81c6541f4f985dd7eac4494d03d9440 ] + +If thermal_zone_device_register_with_trips() fails after adding +a thermal governor to the thermal zone being registered, the +governor is not removed from it as appropriate which may lead to +a memory leak. + +In turn, thermal_zone_device_unregister() calls thermal_set_governor() +without acquiring the thermal zone lock beforehand which may race with +a governor update via sysfs and may lead to a use-after-free in that +case. + +Address these issues by adding two thermal_set_governor() calls, one to +thermal_release() to remove the governor from the given thermal zone, +and one to the thermal zone registration error path to cover failures +preceding the thermal zone device registration. + +Fixes: e33df1d2f3a0 ("thermal: let governors have private data for each thermal zone") +Cc: All applicable +Signed-off-by: Rafael J. Wysocki +Link: https://patch.msgid.link/5092923.31r3eYUQgx@rafael.j.wysocki +[ adapted context for missing mutex_destroy/complete ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/thermal/thermal_core.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/drivers/thermal/thermal_core.c ++++ b/drivers/thermal/thermal_core.c +@@ -986,6 +986,7 @@ static void thermal_release(struct devic + sizeof("thermal_zone") - 1)) { + tz = to_thermal_zone(dev); + thermal_zone_destroy_device_groups(tz); ++ thermal_set_governor(tz, NULL); + kfree(tz); + } else if (!strncmp(dev_name(dev), "cooling_device", + sizeof("cooling_device") - 1)) { +@@ -1447,8 +1448,10 @@ thermal_zone_device_register(const char + /* sys I/F */ + /* Add nodes that are always present via .groups */ + result = thermal_zone_create_device_groups(tz, mask); +- if (result) ++ if (result) { ++ thermal_set_governor(tz, NULL); + goto remove_id; ++ } + + /* A new thermal zone needs to be updated anyway. */ + atomic_set(&tz->need_update, 1); +@@ -1571,8 +1574,6 @@ void thermal_zone_device_unregister(stru + + cancel_delayed_work_sync(&tz->poll_queue); + +- thermal_set_governor(tz, NULL); +- + thermal_remove_hwmon_sysfs(tz); + ida_simple_remove(&thermal_tz_ida, tz->id); + ida_destroy(&tz->ida); diff --git a/queue-5.10/thunderbolt-property-cap-recursion-depth-in-__tb_property_parse_dir.patch b/queue-5.10/thunderbolt-property-cap-recursion-depth-in-__tb_property_parse_dir.patch new file mode 100644 index 0000000000..6d9065a23b --- /dev/null +++ b/queue-5.10/thunderbolt-property-cap-recursion-depth-in-__tb_property_parse_dir.patch @@ -0,0 +1,112 @@ +From stable+bounces-260840-greg=kroah.com@vger.kernel.org Sat Jun 6 07:49:11 2026 +From: Sasha Levin +Date: Fri, 5 Jun 2026 22:18:56 -0400 +Subject: thunderbolt: property: Cap recursion depth in __tb_property_parse_dir() +To: stable@vger.kernel.org +Cc: Michael Bommarito , Mika Westerberg , Sasha Levin +Message-ID: <20260606021856.2488432-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit 928abe19fbf0127003abcb1ea69cabc1c897d0ab ] + +A DIRECTORY entry's value field is used as the dir_offset for a +recursive call into __tb_property_parse_dir() with no depth counter. +A crafted peer that chains DIRECTORY entries into a back-reference +loop drives the parser until the kernel stack is exhausted and the +guard page fires. Any untrusted XDomain peer (cable, dock, in-line +inspector, adjacent host) that reaches the PROPERTIES_REQUEST +control-plane exchange can trigger this without authentication. + +Thread a depth counter through tb_property_parse() and +__tb_property_parse_dir(), and reject blocks that exceed +TB_PROPERTY_MAX_DEPTH = 8. That is comfortably larger than any +observed legitimate XDomain layout. + +Operators who do not need XDomain host-to-host discovery can disable +the path entirely with thunderbolt.xdomain=0 on the kernel command +line. + +Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-6 +Assisted-by: Codex:gpt-5-4 +Signed-off-by: Michael Bommarito +Signed-off-by: Mika Westerberg +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/thunderbolt/property.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +--- a/drivers/thunderbolt/property.c ++++ b/drivers/thunderbolt/property.c +@@ -35,10 +35,11 @@ struct tb_property_dir_entry { + }; + + #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 ++#define TB_PROPERTY_MAX_DEPTH 8 + + static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, + size_t block_len, unsigned int dir_offset, size_t dir_len, +- bool is_root); ++ bool is_root, unsigned int depth); + + static inline void parse_dwdata(void *dst, const void *src, size_t dwords) + { +@@ -99,7 +100,8 @@ tb_property_alloc(const char *key, enum + } + + static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, +- const struct tb_property_entry *entry) ++ const struct tb_property_entry *entry, ++ unsigned int depth) + { + char key[TB_PROPERTY_KEY_SIZE + 1]; + struct tb_property *property; +@@ -120,7 +122,7 @@ static struct tb_property *tb_property_p + switch (property->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + dir = __tb_property_parse_dir(block, block_len, entry->value, +- entry->length, false); ++ entry->length, false, depth + 1); + if (!dir) { + kfree(property); + return NULL; +@@ -165,13 +167,17 @@ static struct tb_property *tb_property_p + } + + static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, +- size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root) ++ size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root, ++ unsigned int depth) + { + const struct tb_property_entry *entries; + size_t i, content_len, nentries; + unsigned int content_offset; + struct tb_property_dir *dir; + ++ if (depth > TB_PROPERTY_MAX_DEPTH) ++ return NULL; ++ + dir = kzalloc(sizeof(*dir), GFP_KERNEL); + if (!dir) + return NULL; +@@ -206,7 +212,7 @@ static struct tb_property_dir *__tb_prop + for (i = 0; i < nentries; i++) { + struct tb_property *property; + +- property = tb_property_parse(block, block_len, &entries[i]); ++ property = tb_property_parse(block, block_len, &entries[i], depth); + if (!property) { + tb_property_free_dir(dir); + return NULL; +@@ -243,7 +249,7 @@ struct tb_property_dir *tb_property_pars + return NULL; + + return __tb_property_parse_dir(block, block_len, 0, rootdir->length, +- true); ++ true, 0); + } + + /** diff --git a/queue-5.10/tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch b/queue-5.10/tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch new file mode 100644 index 0000000000..bfc7210e90 --- /dev/null +++ b/queue-5.10/tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch @@ -0,0 +1,56 @@ +From stable+bounces-245167-greg=kroah.com@vger.kernel.org Mon May 11 15:09:52 2026 +From: Sasha Levin +Date: Mon, 11 May 2026 05:11:47 -0400 +Subject: tracepoint: balance regfunc() on func_add() failure in tracepoint_add_func() +To: stable@vger.kernel.org +Cc: David Carlier , Masami Hiramatsu , Mathieu Desnoyers , "Steven Rostedt (Google)" , Sasha Levin +Message-ID: <20260511091147.1436423-1-sashal@kernel.org> + +From: David Carlier + +[ Upstream commit fad217e16fded7f3c09f8637b0f6a224d58b5f2e ] + +When a tracepoint goes through the 0 -> 1 transition, tracepoint_add_func() +invokes the subsystem's ext->regfunc() before attempting to install the +new probe via func_add(). If func_add() then fails (for example, when +allocate_probes() cannot allocate a new probe array under memory pressure +and returns -ENOMEM), the function returns the error without calling the +matching ext->unregfunc(), leaving the side effects of regfunc() behind +with no installed probe to justify them. + +For syscall tracepoints this is particularly unpleasant: syscall_regfunc() +bumps sys_tracepoint_refcount and sets SYSCALL_TRACEPOINT on every task. +After a leaked failure, the refcount is stuck at a non-zero value with no +consumer, and every task continues paying the syscall trace entry/exit +overhead until reboot. Other subsystems providing regfunc()/unregfunc() +pairs exhibit similarly scoped persistent state. + +Mirror the existing 1 -> 0 cleanup and call ext->unregfunc() in the +func_add() error path, gated on the same condition used there so the +unwind is symmetric with the registration. + +Fixes: 8cf868affdc4 ("tracing: Have the reg function allow to fail") +Cc: stable@vger.kernel.org +Cc: Masami Hiramatsu +Cc: Mathieu Desnoyers +Link: https://patch.msgid.link/20260413190601.21993-1-devnexen@gmail.com +Signed-off-by: David Carlier +Signed-off-by: Steven Rostedt (Google) +[ changed `tp->ext->unregfunc` to `tp->unregfunc` to match older struct layout ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/tracepoint.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/kernel/tracepoint.c ++++ b/kernel/tracepoint.c +@@ -358,6 +358,8 @@ static int tracepoint_add_func(struct tr + lockdep_is_held(&tracepoints_mutex)); + old = func_add(&tp_funcs, func, prio); + if (IS_ERR(old)) { ++ if (tp->unregfunc && !static_key_enabled(&tp->key)) ++ tp->unregfunc(); + WARN_ON_ONCE(warn && PTR_ERR(old) != -ENOMEM); + return PTR_ERR(old); + } diff --git a/queue-5.10/tracing-probes-limit-size-of-event-probe-to-3k.patch b/queue-5.10/tracing-probes-limit-size-of-event-probe-to-3k.patch new file mode 100644 index 0000000000..53a093d5b6 --- /dev/null +++ b/queue-5.10/tracing-probes-limit-size-of-event-probe-to-3k.patch @@ -0,0 +1,68 @@ +From stable+bounces-247838-greg=kroah.com@vger.kernel.org Fri May 15 21:20:01 2026 +From: Sasha Levin +Date: Fri, 15 May 2026 11:36:12 -0400 +Subject: tracing/probes: Limit size of event probe to 3K +To: stable@vger.kernel.org +Cc: Steven Rostedt , Mathieu Desnoyers , "Masami Hiramatsu (Google)" , Sasha Levin +Message-ID: <20260515153612.3302611-1-sashal@kernel.org> + +From: Steven Rostedt + +[ Upstream commit b2aa3b4d64e460ac606f386c24e7d8a873ce6f1a ] + +There currently isn't a max limit an event probe can be. One could make an +event greater than PAGE_SIZE, which makes the event useless because if +it's bigger than the max event that can be recorded into the ring buffer, +then it will never be recorded. + +A event probe should never need to be greater than 3K, so make that the +max size. As long as the max is less than the max that can be recorded +onto the ring buffer, it should be fine. + +Cc: stable@vger.kernel.org +Cc: Mathieu Desnoyers +Acked-by: Masami Hiramatsu (Google) +Fixes: 93ccae7a22274 ("tracing/kprobes: Support basic types on dynamic events") +Link: https://patch.msgid.link/20260428122302.706610ba@gandalf.local.home +Signed-off-by: Steven Rostedt +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + kernel/trace/trace_probe.c | 5 +++++ + kernel/trace/trace_probe.h | 4 +++- + 2 files changed, 8 insertions(+), 1 deletion(-) + +--- a/kernel/trace/trace_probe.c ++++ b/kernel/trace/trace_probe.c +@@ -610,6 +610,11 @@ static int traceprobe_parse_probe_arg_bo + parg->offset = *size; + *size += parg->type->size * (parg->count ?: 1); + ++ if (*size > MAX_PROBE_EVENT_SIZE) { ++ trace_probe_log_err(offset, EVENT_TOO_BIG); ++ return -E2BIG; ++ } ++ + if (parg->count) { + len = strlen(parg->type->fmttype) + 6; + parg->fmt = kmalloc(len, GFP_KERNEL); +--- a/kernel/trace/trace_probe.h ++++ b/kernel/trace/trace_probe.h +@@ -33,6 +33,7 @@ + #define MAX_ARRAY_LEN 64 + #define MAX_ARG_NAME_LEN 32 + #define MAX_STRING_SIZE PATH_MAX ++#define MAX_PROBE_EVENT_SIZE 3072 + + /* Reserved field names */ + #define FIELD_STRING_IP "__probe_ip" +@@ -439,7 +440,8 @@ extern int traceprobe_define_arg_fields( + C(FAIL_REG_PROBE, "Failed to register probe event"),\ + C(DIFF_PROBE_TYPE, "Probe type is different from existing probe"),\ + C(DIFF_ARG_TYPE, "Argument type or name is different from existing probe"),\ +- C(SAME_PROBE, "There is already the exact same probe event"), ++ C(SAME_PROBE, "There is already the exact same probe event"),\ ++ C(EVENT_TOO_BIG, "Event too big (too many fields?)"), + + #undef C + #define C(a, b) TP_ERR_##a diff --git a/queue-5.10/tty-serial-qcom-geni-serial-align-define-values.patch b/queue-5.10/tty-serial-qcom-geni-serial-align-define-values.patch new file mode 100644 index 0000000000..43eab5091f --- /dev/null +++ b/queue-5.10/tty-serial-qcom-geni-serial-align-define-values.patch @@ -0,0 +1,115 @@ +From stable+bounces-260873-greg=kroah.com@vger.kernel.org Sat Jun 6 18:02:54 2026 +From: Sasha Levin +Date: Sat, 6 Jun 2026 08:31:59 -0400 +Subject: tty: serial: qcom-geni-serial: align #define values +To: stable@vger.kernel.org +Cc: Bartosz Golaszewski , Konrad Dybcio , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260606123200.2861082-2-sashal@kernel.org> + +From: Bartosz Golaszewski + +[ Upstream commit 6cde11dbf4b65170eeefba48df730c93d75e01a3 ] + +Keep the #define symbols aligned for better readability. + +Signed-off-by: Bartosz Golaszewski +Reviewed-by: Konrad Dybcio +Link: https://lore.kernel.org/r/20221229155030.418800-5-brgl@bgdev.pl +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: ca2584d841b6 ("serial: qcom-geni: fix UART_RX_PAR_EN bit position") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/qcom_geni_serial.c | 60 +++++++++++++++++----------------- + 1 file changed, 30 insertions(+), 30 deletions(-) + +--- a/drivers/tty/serial/qcom_geni_serial.c ++++ b/drivers/tty/serial/qcom_geni_serial.c +@@ -35,57 +35,57 @@ + #define SE_UART_MANUAL_RFR 0x2ac + + /* SE_UART_TRANS_CFG */ +-#define UART_TX_PAR_EN BIT(0) +-#define UART_CTS_MASK BIT(1) ++#define UART_TX_PAR_EN BIT(0) ++#define UART_CTS_MASK BIT(1) + + /* SE_UART_TX_STOP_BIT_LEN */ +-#define TX_STOP_BIT_LEN_1 0 +-#define TX_STOP_BIT_LEN_2 2 ++#define TX_STOP_BIT_LEN_1 0 ++#define TX_STOP_BIT_LEN_2 2 + + /* SE_UART_RX_TRANS_CFG */ +-#define UART_RX_PAR_EN BIT(3) ++#define UART_RX_PAR_EN BIT(3) + + /* SE_UART_RX_WORD_LEN */ +-#define RX_WORD_LEN_MASK GENMASK(9, 0) ++#define RX_WORD_LEN_MASK GENMASK(9, 0) + + /* SE_UART_RX_STALE_CNT */ +-#define RX_STALE_CNT GENMASK(23, 0) ++#define RX_STALE_CNT GENMASK(23, 0) + + /* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */ +-#define PAR_CALC_EN BIT(0) +-#define PAR_EVEN 0x00 +-#define PAR_ODD 0x01 +-#define PAR_SPACE 0x10 ++#define PAR_CALC_EN BIT(0) ++#define PAR_EVEN 0x00 ++#define PAR_ODD 0x01 ++#define PAR_SPACE 0x10 + + /* SE_UART_MANUAL_RFR register fields */ +-#define UART_MANUAL_RFR_EN BIT(31) +-#define UART_RFR_NOT_READY BIT(1) +-#define UART_RFR_READY BIT(0) ++#define UART_MANUAL_RFR_EN BIT(31) ++#define UART_RFR_NOT_READY BIT(1) ++#define UART_RFR_READY BIT(0) + + /* UART M_CMD OP codes */ +-#define UART_START_TX 0x1 ++#define UART_START_TX 0x1 + /* UART S_CMD OP codes */ +-#define UART_START_READ 0x1 ++#define UART_START_READ 0x1 + +-#define UART_OVERSAMPLING 32 +-#define STALE_TIMEOUT 16 +-#define DEFAULT_BITS_PER_CHAR 10 +-#define GENI_UART_CONS_PORTS 1 +-#define GENI_UART_PORTS 3 +-#define DEF_FIFO_DEPTH_WORDS 16 +-#define DEF_TX_WM 2 +-#define DEF_FIFO_WIDTH_BITS 32 +-#define UART_RX_WM 2 ++#define UART_OVERSAMPLING 32 ++#define STALE_TIMEOUT 16 ++#define DEFAULT_BITS_PER_CHAR 10 ++#define GENI_UART_CONS_PORTS 1 ++#define GENI_UART_PORTS 3 ++#define DEF_FIFO_DEPTH_WORDS 16 ++#define DEF_TX_WM 2 ++#define DEF_FIFO_WIDTH_BITS 32 ++#define UART_RX_WM 2 + + /* SE_UART_LOOPBACK_CFG */ +-#define RX_TX_SORTED BIT(0) +-#define CTS_RTS_SORTED BIT(1) +-#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED) ++#define RX_TX_SORTED BIT(0) ++#define CTS_RTS_SORTED BIT(1) ++#define RX_TX_CTS_RTS_SORTED (RX_TX_SORTED | CTS_RTS_SORTED) + + /* UART pin swap value */ +-#define DEFAULT_IO_MACRO_IO0_IO1_MASK GENMASK(3, 0) ++#define DEFAULT_IO_MACRO_IO0_IO1_MASK GENMASK(3, 0) + #define IO_MACRO_IO0_SEL 0x3 +-#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4) ++#define DEFAULT_IO_MACRO_IO2_IO3_MASK GENMASK(15, 4) + #define IO_MACRO_IO2_IO3_SWAP 0x4640 + + /* We always configure 4 bytes per FIFO word */ diff --git a/queue-5.10/tty-serial-qcom-geni-serial-remove-unused-symbols.patch b/queue-5.10/tty-serial-qcom-geni-serial-remove-unused-symbols.patch new file mode 100644 index 0000000000..a57f2e6d0f --- /dev/null +++ b/queue-5.10/tty-serial-qcom-geni-serial-remove-unused-symbols.patch @@ -0,0 +1,73 @@ +From stable+bounces-260872-greg=kroah.com@vger.kernel.org Sat Jun 6 18:02:18 2026 +From: Sasha Levin +Date: Sat, 6 Jun 2026 08:31:58 -0400 +Subject: tty: serial: qcom-geni-serial: remove unused symbols +To: stable@vger.kernel.org +Cc: Bartosz Golaszewski , Konrad Dybcio , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260606123200.2861082-1-sashal@kernel.org> + +From: Bartosz Golaszewski + +[ Upstream commit 68c6bd92c86cbc4937834c79963b27c77ee3bf51 ] + +Drop all unused symbols from the driver. + +Signed-off-by: Bartosz Golaszewski +Reviewed-by: Konrad Dybcio +Link: https://lore.kernel.org/r/20221229155030.418800-4-brgl@bgdev.pl +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: ca2584d841b6 ("serial: qcom-geni: fix UART_RX_PAR_EN bit position") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/serial/qcom_geni_serial.c | 15 --------------- + 1 file changed, 15 deletions(-) + +--- a/drivers/tty/serial/qcom_geni_serial.c ++++ b/drivers/tty/serial/qcom_geni_serial.c +@@ -38,20 +38,11 @@ + #define UART_TX_PAR_EN BIT(0) + #define UART_CTS_MASK BIT(1) + +-/* SE_UART_TX_WORD_LEN */ +-#define TX_WORD_LEN_MSK GENMASK(9, 0) +- + /* SE_UART_TX_STOP_BIT_LEN */ +-#define TX_STOP_BIT_LEN_MSK GENMASK(23, 0) + #define TX_STOP_BIT_LEN_1 0 +-#define TX_STOP_BIT_LEN_1_5 1 + #define TX_STOP_BIT_LEN_2 2 + +-/* SE_UART_TX_TRANS_LEN */ +-#define TX_TRANS_LEN_MSK GENMASK(23, 0) +- + /* SE_UART_RX_TRANS_CFG */ +-#define UART_RX_INS_STATUS_BIT BIT(2) + #define UART_RX_PAR_EN BIT(3) + + /* SE_UART_RX_WORD_LEN */ +@@ -62,12 +53,9 @@ + + /* SE_UART_TX_PARITY_CFG/RX_PARITY_CFG */ + #define PAR_CALC_EN BIT(0) +-#define PAR_MODE_MSK GENMASK(2, 1) +-#define PAR_MODE_SHFT 1 + #define PAR_EVEN 0x00 + #define PAR_ODD 0x01 + #define PAR_SPACE 0x10 +-#define PAR_MARK 0x11 + + /* SE_UART_MANUAL_RFR register fields */ + #define UART_MANUAL_RFR_EN BIT(31) +@@ -76,11 +64,8 @@ + + /* UART M_CMD OP codes */ + #define UART_START_TX 0x1 +-#define UART_START_BREAK 0x4 +-#define UART_STOP_BREAK 0x5 + /* UART S_CMD OP codes */ + #define UART_START_READ 0x1 +-#define UART_PARAM 0x1 + + #define UART_OVERSAMPLING 32 + #define STALE_TIMEOUT 16 diff --git a/queue-5.10/udf-fix-partition-descriptor-append-bookkeeping.patch b/queue-5.10/udf-fix-partition-descriptor-append-bookkeeping.patch new file mode 100644 index 0000000000..cbec57c7e0 --- /dev/null +++ b/queue-5.10/udf-fix-partition-descriptor-append-bookkeeping.patch @@ -0,0 +1,61 @@ +From stable+bounces-244858-greg=kroah.com@vger.kernel.org Sat May 9 06:05:45 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 20:35:37 -0400 +Subject: udf: fix partition descriptor append bookkeeping +To: stable@vger.kernel.org +Cc: Seohyeon Maeng , Jan Kara , Sasha Levin +Message-ID: <20260509003537.2360522-1-sashal@kernel.org> + +From: Seohyeon Maeng + +[ Upstream commit 08841b06fa64d8edbd1a21ca6e613420c90cc4b8 ] + +Mounting a crafted UDF image with repeated partition descriptors can +trigger a heap out-of-bounds write in part_descs_loc[]. + +handle_partition_descriptor() deduplicates entries by partition number, +but appended slots never record partnum. As a result duplicate +Partition Descriptors are appended repeatedly and num_part_descs keeps +growing. + +Once the table is full, the growth path still sizes the allocation from +partnum even though inserts are indexed by num_part_descs. If partnum is +already aligned to PART_DESC_ALLOC_STEP, ALIGN(partnum, step) can keep +the old capacity and the next append writes past the end of the table. + +Store partnum in the appended slot and size growth from the next append +count so deduplication and capacity tracking follow the same model. + +Fixes: ee4af50ca94f ("udf: Fix mounting of Win7 created UDF filesystems") +Cc: stable@vger.kernel.org +Signed-off-by: Seohyeon Maeng +Link: https://patch.msgid.link/20260310081652.21220-1-bioloidgp@gmail.com +Signed-off-by: Jan Kara +[ replaced kzalloc_objs() helper with equivalent kcalloc() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/udf/super.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/fs/udf/super.c ++++ b/fs/udf/super.c +@@ -1657,8 +1657,9 @@ static struct udf_vds_record *handle_par + return &(data->part_descs_loc[i].rec); + if (data->num_part_descs >= data->size_part_descs) { + struct part_desc_seq_scan_data *new_loc; +- unsigned int new_size = ALIGN(partnum, PART_DESC_ALLOC_STEP); ++ unsigned int new_size; + ++ new_size = data->num_part_descs + PART_DESC_ALLOC_STEP; + new_loc = kcalloc(new_size, sizeof(*new_loc), GFP_KERNEL); + if (!new_loc) + return ERR_PTR(-ENOMEM); +@@ -1668,6 +1669,7 @@ static struct udf_vds_record *handle_par + data->part_descs_loc = new_loc; + data->size_part_descs = new_size; + } ++ data->part_descs_loc[data->num_part_descs].partnum = partnum; + return &(data->part_descs_loc[data->num_part_descs++].rec); + } + diff --git a/queue-5.10/usb-dwc3-move-guid-programming-after-phy-initialization.patch b/queue-5.10/usb-dwc3-move-guid-programming-after-phy-initialization.patch new file mode 100644 index 0000000000..aee2f95c7f --- /dev/null +++ b/queue-5.10/usb-dwc3-move-guid-programming-after-phy-initialization.patch @@ -0,0 +1,63 @@ +From sashal@kernel.org Wed May 13 19:39:16 2026 +From: Sasha Levin +Date: Wed, 13 May 2026 10:09:13 -0400 +Subject: usb: dwc3: Move GUID programming after PHY initialization +To: stable@vger.kernel.org +Cc: Selvarasu Ganesan , stable , Pritam Manohar Sutar , Thinh Nguyen , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260513140913.3746791-1-sashal@kernel.org> + +From: Selvarasu Ganesan + +[ Upstream commit aad35f9c926ec220b0742af1ada45666ae667956 ] + +The Linux Version Code is currently written to the GUID register before +PHY initialization. Certain PHY implementations (such as Synopsys eUSB +PHY performing link_sw_reset) clear the GUID register to its default +value during initialization, causing the kernel version information to +be lost. + +Move the GUID register programming to occur after PHY initialization +completes to ensure the Linux version information persists. + +Fixes: fa0ea13e9f1c ("usb: dwc3: core: write LINUX_VERSION_CODE to our GUID register") +Cc: stable +Reported-by: Pritam Manohar Sutar +Signed-off-by: Selvarasu Ganesan +Acked-by: Thinh Nguyen +Link: https://patch.msgid.link/20260417063314.2359-1-selvarasu.g@samsung.com +Signed-off-by: Greg Kroah-Hartman +[ adapted dwc3_writel(dwc, ...) to dwc3_writel(dwc->regs, ...) ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/dwc3/core.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +--- a/drivers/usb/dwc3/core.c ++++ b/drivers/usb/dwc3/core.c +@@ -978,12 +978,6 @@ static int dwc3_core_init(struct dwc3 *d + + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + +- /* +- * Write Linux Version Code to our GUID register so it's easy to figure +- * out which kernel version a bug was found. +- */ +- dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE); +- + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; +@@ -1023,6 +1017,12 @@ static int dwc3_core_init(struct dwc3 *d + if (ret) + goto err1; + ++ /* ++ * Write Linux Version Code to our GUID register so it's easy to figure ++ * out which kernel version a bug was found. ++ */ ++ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE); ++ + dwc3_core_setup_global_control(dwc); + dwc3_core_num_eps(dwc); + diff --git a/queue-5.10/usb-typec-ucsi-check-if-power-role-change-actually-happened-before-handling.patch b/queue-5.10/usb-typec-ucsi-check-if-power-role-change-actually-happened-before-handling.patch new file mode 100644 index 0000000000..8592bf0b9d --- /dev/null +++ b/queue-5.10/usb-typec-ucsi-check-if-power-role-change-actually-happened-before-handling.patch @@ -0,0 +1,69 @@ +From stable+bounces-260839-greg=kroah.com@vger.kernel.org Sat Jun 6 07:48:50 2026 +From: Sasha Levin +Date: Fri, 5 Jun 2026 22:18:44 -0400 +Subject: usb: typec: ucsi: Check if power role change actually happened before handling +To: stable@vger.kernel.org +Cc: Myrrh Periwinkle , stable , Sergey Senozhatsky , Heikki Krogerus , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260606021844.2487798-1-sashal@kernel.org> + +From: Myrrh Periwinkle + +[ Upstream commit b80e7d34c7ea6a564525119d6138fbb577a23dba ] + +The CrOS EC may send a connector status change event with the power +direction changed flag set even if the power direction hasn't actually +changed after initiating a SET_PDR command internally [1]. In practice +this happens on every system suspend due to other changes performed by +the EC [2][3][4], causing suspend to fail. + +Fix this by checking if the power role change actually happened before +handling it. + +[1]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=1689;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 +[2]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=3923;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 +[3]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=5094;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 +[4]: https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform/ec/zephyr/subsys/pd_controller/pdc_power_mgmt.c;l=2229;drc=2d5a1cffce4e5ac8a39442cb3b764d2d5e1cf794 + +Cc: stable +Fixes: 7616f006db07 ("usb: typec: ucsi: Update power_supply on power role change") +Signed-off-by: Myrrh Periwinkle +Reported-and-tested-by: Sergey Senozhatsky +Reviewed-by: Heikki Krogerus +Link: https://patch.msgid.link/20260519-ucsi-fix-2-v1-1-6f1239535187@qtmlabs.xyz +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/typec/ucsi/ucsi.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -656,7 +656,7 @@ static void ucsi_handle_connector_change + struct ucsi *ucsi = con->ucsi; + struct ucsi_connector_status pre_ack_status; + struct ucsi_connector_status post_ack_status; +- enum typec_role role; ++ enum typec_role role, prev_role; + u16 inferred_changes; + u16 changed_flags; + u64 command; +@@ -692,6 +692,8 @@ static void ucsi_handle_connector_change + * short transitional changes. + */ + ++ prev_role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR); ++ + /* 1. First UCSI_GET_CONNECTOR_STATUS */ + command = UCSI_GET_CONNECTOR_STATUS | UCSI_CONNECTOR_NUMBER(con->num); + ret = ucsi_send_command(ucsi, command, &pre_ack_status, +@@ -769,7 +771,8 @@ static void ucsi_handle_connector_change + ucsi_port_psy_changed(con); + } + +- if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) { ++ if ((con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) && ++ role != prev_role) { + typec_set_pwr_role(con->port, role); + + /* Complete pending power role swap */ diff --git a/queue-5.10/usb-typec-ucsi-don-t-update-power_supply-on-power-role-change-if-not-connected.patch b/queue-5.10/usb-typec-ucsi-don-t-update-power_supply-on-power-role-change-if-not-connected.patch new file mode 100644 index 0000000000..457fdcacdf --- /dev/null +++ b/queue-5.10/usb-typec-ucsi-don-t-update-power_supply-on-power-role-change-if-not-connected.patch @@ -0,0 +1,43 @@ +From stable+bounces-260897-greg=kroah.com@vger.kernel.org Sat Jun 6 19:54:14 2026 +From: Sasha Levin +Date: Sat, 6 Jun 2026 10:24:04 -0400 +Subject: usb: typec: ucsi: Don't update power_supply on power role change if not connected +To: stable@vger.kernel.org +Cc: Myrrh Periwinkle , stable , Sergey Senozhatsky , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260606142405.3108014-1-sashal@kernel.org> + +From: Myrrh Periwinkle + +[ Upstream commit d98d413ca65d0790a8f3695d0a5845538958ab84 ] + +We only need to update the power_supply on power role change if the port +is connected, because otherwise the online status should be the same for +both cases. + +Cc: stable +Fixes: 7616f006db07 ("usb: typec: ucsi: Update power_supply on power role change") +Signed-off-by: Myrrh Periwinkle +Reported-and-tested-by: Sergey Senozhatsky +Link: https://patch.msgid.link/20260519-ucsi-fix-2-v1-2-6f1239535187@qtmlabs.xyz +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/typec/ucsi/ucsi.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -775,6 +775,12 @@ static void ucsi_handle_connector_change + role != prev_role) { + typec_set_pwr_role(con->port, role); + ++ /* Some power_supply properties vary depending on the power direction when ++ * connected ++ */ ++ if (con->status.flags & UCSI_CONSTAT_CONNECTED) ++ ucsi_port_psy_changed(con); ++ + /* Complete pending power role swap */ + if (!completion_done(&con->complete)) + complete(&con->complete); diff --git a/queue-5.10/use-less-confusing-names-for-iov_iter-direction-initializers.patch b/queue-5.10/use-less-confusing-names-for-iov_iter-direction-initializers.patch new file mode 100644 index 0000000000..035a55b3ea --- /dev/null +++ b/queue-5.10/use-less-confusing-names-for-iov_iter-direction-initializers.patch @@ -0,0 +1,1420 @@ +From stable+bounces-256902-greg=kroah.com@vger.kernel.org Sat May 30 19:50:23 2026 +From: Sasha Levin +Date: Sat, 30 May 2026 10:19:23 -0400 +Subject: use less confusing names for iov_iter direction initializers +To: stable@vger.kernel.org +Cc: Al Viro , Sasha Levin +Message-ID: <20260530141926.2406669-1-sashal@kernel.org> + +From: Al Viro + +[ Upstream commit de4eda9de2d957ef2d6a8365a01e26a435e958cb ] + +READ/WRITE proved to be actively confusing - the meanings are +"data destination, as used with read(2)" and "data source, as +used with write(2)", but people keep interpreting those as +"we read data from it" and "we write data to it", i.e. exactly +the wrong way. + +Call them ITER_DEST and ITER_SOURCE - at least that is harder +to misinterpret... + +Signed-off-by: Al Viro +Stable-dep-of: a4f0b001782b ("vsock/virtio: reset connection on receiving queue overflow") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kernel/cpu/microcode/intel.c | 2 +- + crypto/testmgr.c | 4 ++-- + drivers/block/drbd/drbd_main.c | 2 +- + drivers/block/drbd/drbd_receiver.c | 2 +- + drivers/block/loop.c | 14 +++++++------- + drivers/block/nbd.c | 10 +++++----- + drivers/char/random.c | 4 ++-- + drivers/fsi/fsi-sbefifo.c | 6 +++--- + drivers/infiniband/ulp/rtrs/rtrs-clt.c | 2 +- + drivers/isdn/mISDN/l1oip_core.c | 2 +- + drivers/misc/vmw_vmci/vmci_queue_pair.c | 6 +++--- + drivers/net/ppp/ppp_generic.c | 2 +- + drivers/nvme/host/tcp.c | 4 ++-- + drivers/nvme/target/io-cmd-file.c | 4 ++-- + drivers/nvme/target/tcp.c | 2 +- + drivers/scsi/sg.c | 2 +- + drivers/target/iscsi/iscsi_target_util.c | 4 ++-- + drivers/target/target_core_file.c | 2 +- + drivers/usb/usbip/usbip_common.c | 2 +- + drivers/vhost/net.c | 6 +++--- + drivers/vhost/scsi.c | 10 +++++----- + drivers/vhost/vhost.c | 6 +++--- + drivers/vhost/vringh.c | 4 ++-- + drivers/vhost/vsock.c | 4 ++-- + drivers/xen/pvcalls-back.c | 8 ++++---- + fs/9p/vfs_addr.c | 4 ++-- + fs/9p/vfs_dir.c | 2 +- + fs/9p/xattr.c | 4 ++-- + fs/afs/cmservice.c | 2 +- + fs/afs/internal.h | 4 ++-- + fs/afs/rxrpc.c | 12 ++++++------ + fs/aio.c | 4 ++-- + fs/ceph/file.c | 4 ++-- + fs/cifs/connect.c | 6 +++--- + fs/cifs/file.c | 4 ++-- + fs/cifs/smb2ops.c | 4 ++-- + fs/cifs/transport.c | 6 +++--- + fs/nfsd/vfs.c | 4 ++-- + fs/ocfs2/cluster/tcp.c | 2 +- + fs/orangefs/inode.c | 6 +++--- + fs/read_write.c | 12 ++++++------ + fs/seq_file.c | 2 +- + fs/splice.c | 10 +++++----- + include/linux/uio.h | 3 +++ + mm/madvise.c | 2 +- + mm/page_io.c | 2 +- + mm/process_vm_access.c | 2 +- + net/9p/client.c | 2 +- + net/bluetooth/6lowpan.c | 2 +- + net/bluetooth/a2mp.c | 2 +- + net/bluetooth/smp.c | 2 +- + net/ipv4/tcp.c | 2 +- + net/netfilter/ipvs/ip_vs_sync.c | 2 +- + net/smc/smc_clc.c | 6 +++--- + net/socket.c | 12 ++++++------ + net/sunrpc/socklib.c | 6 +++--- + net/sunrpc/svcsock.c | 4 ++-- + net/sunrpc/xprtsock.c | 6 +++--- + net/tipc/topsrv.c | 2 +- + net/tls/tls_device.c | 4 ++-- + net/xfrm/espintcp.c | 2 +- + security/keys/keyctl.c | 4 ++-- + 62 files changed, 138 insertions(+), 135 deletions(-) + +--- a/arch/x86/kernel/cpu/microcode/intel.c ++++ b/arch/x86/kernel/cpu/microcode/intel.c +@@ -940,7 +940,7 @@ static enum ucode_state request_microcod + + kvec.iov_base = (void *)firmware->data; + kvec.iov_len = firmware->size; +- iov_iter_kvec(&iter, WRITE, &kvec, 1, firmware->size); ++ iov_iter_kvec(&iter, ITER_SOURCE, &kvec, 1, firmware->size); + ret = generic_load_microcode(cpu, &iter); + + release_firmware(firmware); +--- a/crypto/testmgr.c ++++ b/crypto/testmgr.c +@@ -747,7 +747,7 @@ static int build_cipher_test_sglists(str + struct iov_iter input; + int err; + +- iov_iter_kvec(&input, WRITE, inputs, nr_inputs, src_total_len); ++ iov_iter_kvec(&input, ITER_SOURCE, inputs, nr_inputs, src_total_len); + err = build_test_sglist(&tsgls->src, cfg->src_divs, alignmask, + cfg->inplace ? + max(dst_total_len, src_total_len) : +@@ -1130,7 +1130,7 @@ static int build_hash_sglist(struct test + + kv.iov_base = (void *)vec->plaintext; + kv.iov_len = vec->psize; +- iov_iter_kvec(&input, WRITE, &kv, 1, vec->psize); ++ iov_iter_kvec(&input, ITER_SOURCE, &kv, 1, vec->psize); + return build_test_sglist(tsgl, cfg->src_divs, alignmask, vec->psize, + &input, divs); + } +--- a/drivers/block/drbd/drbd_main.c ++++ b/drivers/block/drbd/drbd_main.c +@@ -1857,7 +1857,7 @@ int drbd_send(struct drbd_connection *co + + /* THINK if (signal_pending) return ... ? */ + +- iov_iter_kvec(&msg.msg_iter, WRITE, &iov, 1, size); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &iov, 1, size); + + if (sock == connection->data.socket) { + rcu_read_lock(); +--- a/drivers/block/drbd/drbd_receiver.c ++++ b/drivers/block/drbd/drbd_receiver.c +@@ -505,7 +505,7 @@ static int drbd_recv_short(struct socket + struct msghdr msg = { + .msg_flags = (flags ? flags : MSG_WAITALL | MSG_NOSIGNAL) + }; +- iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, size); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, size); + return sock_recvmsg(sock, &msg, msg.msg_flags); + } + +--- a/drivers/block/loop.c ++++ b/drivers/block/loop.c +@@ -265,7 +265,7 @@ static int lo_write_bvec(struct file *fi + struct iov_iter i; + ssize_t bw; + +- iov_iter_bvec(&i, WRITE, bvec, 1, bvec->bv_len); ++ iov_iter_bvec(&i, ITER_SOURCE, bvec, 1, bvec->bv_len); + + file_start_write(file); + bw = vfs_iter_write(file, &i, ppos, 0); +@@ -343,7 +343,7 @@ static int lo_read_simple(struct loop_de + ssize_t len; + + rq_for_each_segment(bvec, rq, iter) { +- iov_iter_bvec(&i, READ, &bvec, 1, bvec.bv_len); ++ iov_iter_bvec(&i, ITER_DEST, &bvec, 1, bvec.bv_len); + len = vfs_iter_read(lo->lo_backing_file, &i, &pos, 0); + if (len < 0) + return len; +@@ -384,7 +384,7 @@ static int lo_read_transfer(struct loop_ + b.bv_offset = 0; + b.bv_len = bvec.bv_len; + +- iov_iter_bvec(&i, READ, &b, 1, b.bv_len); ++ iov_iter_bvec(&i, ITER_DEST, &b, 1, b.bv_len); + len = vfs_iter_read(lo->lo_backing_file, &i, &pos, 0); + if (len < 0) { + ret = len; +@@ -508,7 +508,7 @@ static void lo_rw_aio_complete(struct ki + } + + static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd, +- loff_t pos, bool rw) ++ loff_t pos, int rw) + { + struct iov_iter iter; + struct req_iterator rq_iter; +@@ -566,7 +566,7 @@ static int lo_rw_aio(struct loop_device + if (cmd->css) + kthread_associate_blkcg(cmd->css); + +- if (rw == WRITE) ++ if (rw == ITER_SOURCE) + ret = call_write_iter(file, &cmd->iocb, &iter); + else + ret = call_read_iter(file, &cmd->iocb, &iter); +@@ -611,14 +611,14 @@ static int do_req_filebacked(struct loop + if (lo->transfer) + return lo_write_transfer(lo, rq, pos); + else if (cmd->use_aio) +- return lo_rw_aio(lo, cmd, pos, WRITE); ++ return lo_rw_aio(lo, cmd, pos, ITER_SOURCE); + else + return lo_write_simple(lo, rq, pos); + case REQ_OP_READ: + if (lo->transfer) + return lo_read_transfer(lo, rq, pos); + else if (cmd->use_aio) +- return lo_rw_aio(lo, cmd, pos, READ); ++ return lo_rw_aio(lo, cmd, pos, ITER_DEST); + else + return lo_read_simple(lo, rq, pos); + default: +--- a/drivers/block/nbd.c ++++ b/drivers/block/nbd.c +@@ -543,7 +543,7 @@ static int nbd_send_cmd(struct nbd_devic + u32 nbd_cmd_flags = 0; + int sent = nsock->sent, skip = 0; + +- iov_iter_kvec(&from, WRITE, &iov, 1, sizeof(request)); ++ iov_iter_kvec(&from, ITER_SOURCE, &iov, 1, sizeof(request)); + + type = req_to_nbd_cmd_type(req); + if (type == U32_MAX) +@@ -629,7 +629,7 @@ send_pages: + + dev_dbg(nbd_to_dev(nbd), "request %p: sending %d bytes data\n", + req, bvec.bv_len); +- iov_iter_bvec(&from, WRITE, &bvec, 1, bvec.bv_len); ++ iov_iter_bvec(&from, ITER_SOURCE, &bvec, 1, bvec.bv_len); + if (skip) { + if (skip >= iov_iter_count(&from)) { + skip -= iov_iter_count(&from); +@@ -681,7 +681,7 @@ static int nbd_read_reply(struct nbd_dev + int result; + + reply->magic = 0; +- iov_iter_kvec(&to, READ, &iov, 1, sizeof(*reply)); ++ iov_iter_kvec(&to, ITER_DEST, &iov, 1, sizeof(*reply)); + result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL, NULL); + if (result < 0) { + if (!nbd_disconnected(nbd->config)) +@@ -758,7 +758,7 @@ static struct nbd_cmd *nbd_handle_reply( + struct iov_iter to; + + rq_for_each_segment(bvec, req, iter) { +- iov_iter_bvec(&to, READ, &bvec, 1, bvec.bv_len); ++ iov_iter_bvec(&to, ITER_DEST, &bvec, 1, bvec.bv_len); + result = sock_xmit(nbd, index, 0, &to, MSG_WAITALL, NULL); + if (result < 0) { + dev_err(disk_to_dev(nbd->disk), "Receive data failed (result %d)\n", +@@ -1206,7 +1206,7 @@ static void send_disconnects(struct nbd_ + for (i = 0; i < config->num_connections; i++) { + struct nbd_sock *nsock = config->socks[i]; + +- iov_iter_kvec(&from, WRITE, &iov, 1, sizeof(request)); ++ iov_iter_kvec(&from, ITER_SOURCE, &iov, 1, sizeof(request)); + mutex_lock(&nsock->tx_lock); + ret = sock_xmit(nbd, i, 1, &from, 0, NULL); + if (ret < 0) +--- a/drivers/char/random.c ++++ b/drivers/char/random.c +@@ -1237,7 +1237,7 @@ SYSCALL_DEFINE3(getrandom, char __user * + return ret; + } + +- ret = import_single_range(READ, ubuf, len, &iov, &iter); ++ ret = import_single_range(ITER_DEST, ubuf, len, &iov, &iter); + if (unlikely(ret)) + return ret; + return get_random_bytes_user(&iter); +@@ -1348,7 +1348,7 @@ static long random_ioctl(struct file *f, + return -EINVAL; + if (get_user(len, p++)) + return -EFAULT; +- ret = import_single_range(WRITE, p, len, &iov, &iter); ++ ret = import_single_range(ITER_SOURCE, p, len, &iov, &iter); + if (unlikely(ret)) + return ret; + ret = write_pool_user(&iter); +--- a/drivers/fsi/fsi-sbefifo.c ++++ b/drivers/fsi/fsi-sbefifo.c +@@ -640,7 +640,7 @@ static void sbefifo_collect_async_ffdc(s + } + ffdc_iov.iov_base = ffdc; + ffdc_iov.iov_len = SBEFIFO_MAX_FFDC_SIZE; +- iov_iter_kvec(&ffdc_iter, READ, &ffdc_iov, 1, SBEFIFO_MAX_FFDC_SIZE); ++ iov_iter_kvec(&ffdc_iter, ITER_DEST, &ffdc_iov, 1, SBEFIFO_MAX_FFDC_SIZE); + cmd[0] = cpu_to_be32(2); + cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_SBE_FFDC); + rc = sbefifo_do_command(sbefifo, cmd, 2, &ffdc_iter); +@@ -737,7 +737,7 @@ int sbefifo_submit(struct device *dev, c + rbytes = (*resp_len) * sizeof(__be32); + resp_iov.iov_base = response; + resp_iov.iov_len = rbytes; +- iov_iter_kvec(&resp_iter, READ, &resp_iov, 1, rbytes); ++ iov_iter_kvec(&resp_iter, ITER_DEST, &resp_iov, 1, rbytes); + + /* Perform the command */ + mutex_lock(&sbefifo->lock); +@@ -817,7 +817,7 @@ static ssize_t sbefifo_user_read(struct + /* Prepare iov iterator */ + resp_iov.iov_base = buf; + resp_iov.iov_len = len; +- iov_iter_init(&resp_iter, READ, &resp_iov, 1, len); ++ iov_iter_init(&resp_iter, ITER_DEST, &resp_iov, 1, len); + + /* Perform the command */ + mutex_lock(&sbefifo->lock); +--- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c ++++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c +@@ -902,7 +902,7 @@ static void rtrs_clt_init_req(struct rtr + req->need_inv_comp = false; + req->inv_errno = 0; + +- iov_iter_kvec(&iter, WRITE, vec, 1, usr_len); ++ iov_iter_kvec(&iter, ITER_SOURCE, vec, 1, usr_len); + len = _copy_from_iter(req->iu->buf, usr_len, &iter); + WARN_ON(len != usr_len); + +--- a/drivers/isdn/mISDN/l1oip_core.c ++++ b/drivers/isdn/mISDN/l1oip_core.c +@@ -706,7 +706,7 @@ l1oip_socket_thread(void *data) + printk(KERN_DEBUG "%s: socket created and open\n", + __func__); + while (!signal_pending(current)) { +- iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, recvbuf_size); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, recvbuf_size); + recvlen = sock_recvmsg(socket, &msg, 0); + if (recvlen > 0) { + l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen); +--- a/drivers/misc/vmw_vmci/vmci_queue_pair.c ++++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c +@@ -3028,7 +3028,7 @@ ssize_t vmci_qpair_enqueue(struct vmci_q + if (!qpair || !buf) + return VMCI_ERROR_INVALID_ARGS; + +- iov_iter_kvec(&from, WRITE, &v, 1, buf_size); ++ iov_iter_kvec(&from, ITER_SOURCE, &v, 1, buf_size); + + qp_lock(qpair); + +@@ -3072,7 +3072,7 @@ ssize_t vmci_qpair_dequeue(struct vmci_q + if (!qpair || !buf) + return VMCI_ERROR_INVALID_ARGS; + +- iov_iter_kvec(&to, READ, &v, 1, buf_size); ++ iov_iter_kvec(&to, ITER_DEST, &v, 1, buf_size); + + qp_lock(qpair); + +@@ -3117,7 +3117,7 @@ ssize_t vmci_qpair_peek(struct vmci_qp * + if (!qpair || !buf) + return VMCI_ERROR_INVALID_ARGS; + +- iov_iter_kvec(&to, READ, &v, 1, buf_size); ++ iov_iter_kvec(&to, ITER_DEST, &v, 1, buf_size); + + qp_lock(qpair); + +--- a/drivers/net/ppp/ppp_generic.c ++++ b/drivers/net/ppp/ppp_generic.c +@@ -490,7 +490,7 @@ static ssize_t ppp_read(struct file *fil + ret = -EFAULT; + iov.iov_base = buf; + iov.iov_len = count; +- iov_iter_init(&to, READ, &iov, 1, count); ++ iov_iter_init(&to, ITER_DEST, &iov, 1, count); + if (skb_copy_datagram_iter(skb, 0, &to, skb->len)) + goto outf; + ret = skb->len; +--- a/drivers/nvme/host/tcp.c ++++ b/drivers/nvme/host/tcp.c +@@ -296,7 +296,7 @@ static inline void nvme_tcp_advance_req( + if (!iov_iter_count(&req->iter) && + req->data_sent < req->data_len) { + req->curr_bio = req->curr_bio->bi_next; +- nvme_tcp_init_iter(req, WRITE); ++ nvme_tcp_init_iter(req, ITER_SOURCE); + } + } + +@@ -766,7 +766,7 @@ static int nvme_tcp_recv_data(struct nvm + nvme_tcp_init_recv_ctx(queue); + return -EIO; + } +- nvme_tcp_init_iter(req, READ); ++ nvme_tcp_init_iter(req, ITER_DEST); + } + + /* we can read only from what is left in this bio */ +--- a/drivers/nvme/target/io-cmd-file.c ++++ b/drivers/nvme/target/io-cmd-file.c +@@ -111,10 +111,10 @@ static ssize_t nvmet_file_submit_bvec(st + if (req->cmd->rw.control & cpu_to_le16(NVME_RW_FUA)) + ki_flags |= IOCB_DSYNC; + call_iter = req->ns->file->f_op->write_iter; +- rw = WRITE; ++ rw = ITER_SOURCE; + } else { + call_iter = req->ns->file->f_op->read_iter; +- rw = READ; ++ rw = ITER_DEST; + } + + iov_iter_bvec(&iter, rw, req->f.bvec, nr_segs, count); +--- a/drivers/nvme/target/tcp.c ++++ b/drivers/nvme/target/tcp.c +@@ -339,7 +339,7 @@ static void nvmet_tcp_build_pdu_iovec(st + sg_offset = 0; + } + +- iov_iter_bvec(&cmd->recv_msg.msg_iter, READ, cmd->iov, ++ iov_iter_bvec(&cmd->recv_msg.msg_iter, ITER_DEST, cmd->iov, + nr_pages, cmd->pdu_len); + } + +--- a/drivers/scsi/sg.c ++++ b/drivers/scsi/sg.c +@@ -1753,7 +1753,7 @@ sg_start_req(Sg_request *srp, unsigned c + Sg_scatter_hold *rsv_schp = &sfp->reserve; + struct request_queue *q = sfp->parentdp->device->request_queue; + struct rq_map_data *md, map_data; +- int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; ++ int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? ITER_SOURCE : ITER_DEST; + unsigned char *long_cmdp = NULL; + + SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, +--- a/drivers/target/iscsi/iscsi_target_util.c ++++ b/drivers/target/iscsi/iscsi_target_util.c +@@ -1247,7 +1247,7 @@ int rx_data( + return -1; + + memset(&msg, 0, sizeof(struct msghdr)); +- iov_iter_kvec(&msg.msg_iter, READ, iov, iov_count, data); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, iov, iov_count, data); + + while (msg_data_left(&msg)) { + rx_loop = sock_recvmsg(conn->sock, &msg, MSG_WAITALL); +@@ -1283,7 +1283,7 @@ int tx_data( + + memset(&msg, 0, sizeof(struct msghdr)); + +- iov_iter_kvec(&msg.msg_iter, WRITE, iov, iov_count, data); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, iov, iov_count, data); + + while (msg_data_left(&msg)) { + int tx_loop = sock_sendmsg(conn->sock, &msg); +--- a/drivers/target/target_core_file.c ++++ b/drivers/target/target_core_file.c +@@ -480,7 +480,7 @@ fd_execute_write_same(struct se_cmd *cmd + len += se_dev->dev_attrib.block_size; + } + +- iov_iter_bvec(&iter, WRITE, bvec, nolb, len); ++ iov_iter_bvec(&iter, ITER_SOURCE, bvec, nolb, len); + ret = vfs_iter_write(fd_dev->fd_file, &iter, &pos, 0); + + kfree(bvec); +--- a/drivers/usb/usbip/usbip_common.c ++++ b/drivers/usb/usbip/usbip_common.c +@@ -309,7 +309,7 @@ int usbip_recv(struct socket *sock, void + if (!sock || !buf || !size) + return -EINVAL; + +- iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, size); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, size); + + usbip_dbg_xmit("enter\n"); + +--- a/drivers/vhost/net.c ++++ b/drivers/vhost/net.c +@@ -614,7 +614,7 @@ static size_t init_iov_iter(struct vhost + /* Skip header. TODO: support TSO. */ + size_t len = iov_length(vq->iov, out); + +- iov_iter_init(iter, WRITE, vq->iov, out, len); ++ iov_iter_init(iter, ITER_SOURCE, vq->iov, out, len); + iov_iter_advance(iter, hdr_size); + + return iov_iter_count(iter); +@@ -1188,14 +1188,14 @@ static void handle_rx(struct vhost_net * + msg.msg_control = vhost_net_buf_consume(&nvq->rxq); + /* On overrun, truncate and discard */ + if (unlikely(headcount > UIO_MAXIOV)) { +- iov_iter_init(&msg.msg_iter, READ, vq->iov, 1, 1); ++ iov_iter_init(&msg.msg_iter, ITER_DEST, vq->iov, 1, 1); + err = sock->ops->recvmsg(sock, &msg, + 1, MSG_DONTWAIT | MSG_TRUNC); + pr_debug("Discarded rx packet: len %zd\n", sock_len); + continue; + } + /* We don't need to be notified again. */ +- iov_iter_init(&msg.msg_iter, READ, vq->iov, in, vhost_len); ++ iov_iter_init(&msg.msg_iter, ITER_DEST, vq->iov, in, vhost_len); + fixup = msg.msg_iter; + if (unlikely((vhost_hlen))) { + /* We will supply the header ourselves +--- a/drivers/vhost/scsi.c ++++ b/drivers/vhost/scsi.c +@@ -574,7 +574,7 @@ static void vhost_scsi_complete_cmd_work + memcpy(v_rsp.sense, cmd->tvc_sense_buf, + se_cmd->scsi_sense_length); + +- iov_iter_init(&iov_iter, READ, &cmd->tvc_resp_iov, ++ iov_iter_init(&iov_iter, ITER_DEST, &cmd->tvc_resp_iov, + cmd->tvc_in_iovs, sizeof(v_rsp)); + ret = copy_to_iter(&v_rsp, sizeof(v_rsp), &iov_iter); + if (likely(ret == sizeof(v_rsp))) { +@@ -883,7 +883,7 @@ vhost_scsi_get_desc(struct vhost_scsi *v + * point at the start of the outgoing WRITE payload, if + * DMA_TO_DEVICE is set. + */ +- iov_iter_init(&vc->out_iter, WRITE, vq->iov, vc->out, vc->out_size); ++ iov_iter_init(&vc->out_iter, ITER_SOURCE, vq->iov, vc->out, vc->out_size); + ret = 0; + + done: +@@ -1036,7 +1036,7 @@ vhost_scsi_handle_vq(struct vhost_scsi * + data_direction = DMA_FROM_DEVICE; + exp_data_len = vc.in_size - vc.rsp_size; + +- iov_iter_init(&in_iter, READ, &vq->iov[vc.out], vc.in, ++ iov_iter_init(&in_iter, ITER_DEST, &vq->iov[vc.out], vc.in, + vc.rsp_size + exp_data_len); + iov_iter_advance(&in_iter, vc.rsp_size); + data_iter = in_iter; +@@ -1173,7 +1173,7 @@ vhost_scsi_send_tmf_resp(struct vhost_sc + memset(&rsp, 0, sizeof(rsp)); + rsp.response = tmf_resp_code; + +- iov_iter_init(&iov_iter, READ, resp_iov, in_iovs, sizeof(rsp)); ++ iov_iter_init(&iov_iter, ITER_DEST, resp_iov, in_iovs, sizeof(rsp)); + + ret = copy_to_iter(&rsp, sizeof(rsp), &iov_iter); + if (likely(ret == sizeof(rsp))) +@@ -1268,7 +1268,7 @@ vhost_scsi_send_an_resp(struct vhost_scs + memset(&rsp, 0, sizeof(rsp)); /* event_actual = 0 */ + rsp.response = VIRTIO_SCSI_S_OK; + +- iov_iter_init(&iov_iter, READ, &vq->iov[vc->out], vc->in, sizeof(rsp)); ++ iov_iter_init(&iov_iter, ITER_DEST, &vq->iov[vc->out], vc->in, sizeof(rsp)); + + ret = copy_to_iter(&rsp, sizeof(rsp), &iov_iter); + if (likely(ret == sizeof(rsp))) +--- a/drivers/vhost/vhost.c ++++ b/drivers/vhost/vhost.c +@@ -841,7 +841,7 @@ static int vhost_copy_to_user(struct vho + VHOST_ACCESS_WO); + if (ret < 0) + goto out; +- iov_iter_init(&t, WRITE, vq->iotlb_iov, ret, size); ++ iov_iter_init(&t, ITER_SOURCE, vq->iotlb_iov, ret, size); + ret = copy_to_iter(from, size, &t); + if (ret == size) + ret = 0; +@@ -880,7 +880,7 @@ static int vhost_copy_from_user(struct v + (unsigned long long) size); + goto out; + } +- iov_iter_init(&f, READ, vq->iotlb_iov, ret, size); ++ iov_iter_init(&f, ITER_DEST, vq->iotlb_iov, ret, size); + ret = copy_from_iter(to, size, &f); + if (ret == size) + ret = 0; +@@ -2132,7 +2132,7 @@ static int get_indirect(struct vhost_vir + vq_err(vq, "Translation failure %d in indirect.\n", ret); + return ret; + } +- iov_iter_init(&from, READ, vq->indirect, ret, len); ++ iov_iter_init(&from, ITER_DEST, vq->indirect, ret, len); + count = len / sizeof desc; + /* Buffers are chained via a 16 bit next field, so + * we can have at most 2^16 of these. */ +--- a/drivers/vhost/vringh.c ++++ b/drivers/vhost/vringh.c +@@ -1122,7 +1122,7 @@ static inline int copy_from_iotlb(const + if (ret < 0) + return ret; + +- iov_iter_bvec(&iter, READ, iov, ret, len); ++ iov_iter_bvec(&iter, ITER_DEST, iov, ret, len); + + ret = copy_from_iter(dst, len, &iter); + +@@ -1141,7 +1141,7 @@ static inline int copy_to_iotlb(const st + if (ret < 0) + return ret; + +- iov_iter_bvec(&iter, WRITE, iov, ret, len); ++ iov_iter_bvec(&iter, ITER_SOURCE, iov, ret, len); + + return copy_to_iter(src, len, &iter); + } +--- a/drivers/vhost/vsock.c ++++ b/drivers/vhost/vsock.c +@@ -155,7 +155,7 @@ vhost_transport_do_send_pkt(struct vhost + break; + } + +- iov_iter_init(&iov_iter, READ, &vq->iov[out], in, iov_len); ++ iov_iter_init(&iov_iter, ITER_DEST, &vq->iov[out], in, iov_len); + payload_len = pkt->len - pkt->off; + + /* If the packet is greater than the space available in the +@@ -337,7 +337,7 @@ vhost_vsock_alloc_pkt(struct vhost_virtq + return NULL; + + len = iov_length(vq->iov, out); +- iov_iter_init(&iov_iter, WRITE, vq->iov, out, len); ++ iov_iter_init(&iov_iter, ITER_SOURCE, vq->iov, out, len); + + nbytes = copy_from_iter(&pkt->hdr, sizeof(pkt->hdr), &iov_iter); + if (nbytes != sizeof(pkt->hdr)) { +--- a/drivers/xen/pvcalls-back.c ++++ b/drivers/xen/pvcalls-back.c +@@ -129,13 +129,13 @@ static bool pvcalls_conn_back_read(void + if (masked_prod < masked_cons) { + vec[0].iov_base = data->in + masked_prod; + vec[0].iov_len = wanted; +- iov_iter_kvec(&msg.msg_iter, READ, vec, 1, wanted); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, vec, 1, wanted); + } else { + vec[0].iov_base = data->in + masked_prod; + vec[0].iov_len = array_size - masked_prod; + vec[1].iov_base = data->in; + vec[1].iov_len = wanted - vec[0].iov_len; +- iov_iter_kvec(&msg.msg_iter, READ, vec, 2, wanted); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, vec, 2, wanted); + } + + atomic_set(&map->read, 0); +@@ -188,13 +188,13 @@ static bool pvcalls_conn_back_write(stru + if (pvcalls_mask(prod, array_size) > pvcalls_mask(cons, array_size)) { + vec[0].iov_base = data->out + pvcalls_mask(cons, array_size); + vec[0].iov_len = size; +- iov_iter_kvec(&msg.msg_iter, WRITE, vec, 1, size); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, vec, 1, size); + } else { + vec[0].iov_base = data->out + pvcalls_mask(cons, array_size); + vec[0].iov_len = array_size - pvcalls_mask(cons, array_size); + vec[1].iov_base = data->out; + vec[1].iov_len = size - vec[0].iov_len; +- iov_iter_kvec(&msg.msg_iter, WRITE, vec, 2, size); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, vec, 2, size); + } + + atomic_set(&map->write, 0); +--- a/fs/9p/vfs_addr.c ++++ b/fs/9p/vfs_addr.c +@@ -51,7 +51,7 @@ static int v9fs_fid_readpage(void *data, + if (retval == 0) + return retval; + +- iov_iter_bvec(&to, READ, &bvec, 1, PAGE_SIZE); ++ iov_iter_bvec(&to, ITER_DEST, &bvec, 1, PAGE_SIZE); + + retval = p9_client_read(fid, page_offset(page), &to, &err); + if (err) { +@@ -162,7 +162,7 @@ static int v9fs_vfs_writepage_locked(str + bvec.bv_page = page; + bvec.bv_offset = 0; + bvec.bv_len = len; +- iov_iter_bvec(&from, WRITE, &bvec, 1, len); ++ iov_iter_bvec(&from, ITER_SOURCE, &bvec, 1, len); + + /* We should have writeback_fid always set */ + BUG_ON(!v9inode->writeback_fid); +--- a/fs/9p/vfs_dir.c ++++ b/fs/9p/vfs_dir.c +@@ -108,7 +108,7 @@ static int v9fs_dir_readdir(struct file + if (rdir->tail == rdir->head) { + struct iov_iter to; + int n; +- iov_iter_kvec(&to, READ, &kvec, 1, buflen); ++ iov_iter_kvec(&to, ITER_DEST, &kvec, 1, buflen); + n = p9_client_read(file->private_data, ctx->pos, &to, + &err); + if (err) +--- a/fs/9p/xattr.c ++++ b/fs/9p/xattr.c +@@ -32,7 +32,7 @@ ssize_t v9fs_fid_xattr_get(struct p9_fid + struct iov_iter to; + int err; + +- iov_iter_kvec(&to, READ, &kvec, 1, buffer_size); ++ iov_iter_kvec(&to, ITER_DEST, &kvec, 1, buffer_size); + + attr_fid = p9_client_xattrwalk(fid, name, &attr_size); + if (IS_ERR(attr_fid)) { +@@ -107,7 +107,7 @@ int v9fs_fid_xattr_set(struct p9_fid *fi + struct iov_iter from; + int retval, err; + +- iov_iter_kvec(&from, WRITE, &kvec, 1, value_len); ++ iov_iter_kvec(&from, ITER_SOURCE, &kvec, 1, value_len); + + p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu flags = %d\n", + name, value_len, flags); +--- a/fs/afs/cmservice.c ++++ b/fs/afs/cmservice.c +@@ -298,7 +298,7 @@ static int afs_deliver_cb_callback(struc + if (call->count2 != call->count && call->count2 != 0) + return afs_protocol_error(call, afs_eproto_cb_count); + call->iter = &call->def_iter; +- iov_iter_discard(&call->def_iter, READ, call->count2 * 3 * 4); ++ iov_iter_discard(&call->def_iter, ITER_DEST, call->count2 * 3 * 4); + call->unmarshall++; + + fallthrough; +--- a/fs/afs/internal.h ++++ b/fs/afs/internal.h +@@ -1273,7 +1273,7 @@ static inline void afs_extract_begin(str + { + call->kvec[0].iov_base = buf; + call->kvec[0].iov_len = size; +- iov_iter_kvec(&call->def_iter, READ, call->kvec, 1, size); ++ iov_iter_kvec(&call->def_iter, ITER_DEST, call->kvec, 1, size); + } + + static inline void afs_extract_to_tmp(struct afs_call *call) +@@ -1288,7 +1288,7 @@ static inline void afs_extract_to_tmp64( + + static inline void afs_extract_discard(struct afs_call *call, size_t size) + { +- iov_iter_discard(&call->def_iter, READ, size); ++ iov_iter_discard(&call->def_iter, ITER_DEST, size); + } + + static inline void afs_extract_to_buf(struct afs_call *call, size_t size) +--- a/fs/afs/rxrpc.c ++++ b/fs/afs/rxrpc.c +@@ -302,7 +302,7 @@ static void afs_load_bvec(struct afs_cal + offset = 0; + } + +- iov_iter_bvec(&msg->msg_iter, WRITE, bv, nr, bytes); ++ iov_iter_bvec(&msg->msg_iter, ITER_SOURCE, bv, nr, bytes); + } + + /* +@@ -437,7 +437,7 @@ void afs_make_call(struct afs_addr_curso + + msg.msg_name = NULL; + msg.msg_namelen = 0; +- iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, call->request_size); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, iov, 1, call->request_size); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_WAITALL | (call->send_pages ? MSG_MORE : 0); +@@ -467,7 +467,7 @@ error_do_abort: + rxrpc_kernel_abort_call(call->net->socket, rxcall, + RX_USER_ABORT, ret, "KSD"); + } else { +- iov_iter_kvec(&msg.msg_iter, READ, NULL, 0, 0); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, NULL, 0, 0); + rxrpc_kernel_recv_data(call->net->socket, rxcall, + &msg.msg_iter, false, + &call->abort_code, &call->service_id); +@@ -517,7 +517,7 @@ static void afs_deliver_to_call(struct a + state == AFS_CALL_SV_AWAIT_ACK + ) { + if (state == AFS_CALL_SV_AWAIT_ACK) { +- iov_iter_kvec(&call->def_iter, READ, NULL, 0, 0); ++ iov_iter_kvec(&call->def_iter, ITER_DEST, NULL, 0, 0); + ret = rxrpc_kernel_recv_data(call->net->socket, + call->rxcall, &call->def_iter, + false, &remote_abort, +@@ -854,7 +854,7 @@ void afs_send_empty_reply(struct afs_cal + + msg.msg_name = NULL; + msg.msg_namelen = 0; +- iov_iter_kvec(&msg.msg_iter, WRITE, NULL, 0, 0); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, NULL, 0, 0); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; +@@ -894,7 +894,7 @@ void afs_send_simple_reply(struct afs_ca + iov[0].iov_len = len; + msg.msg_name = NULL; + msg.msg_namelen = 0; +- iov_iter_kvec(&msg.msg_iter, WRITE, iov, 1, len); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, iov, 1, len); + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1547,7 +1547,7 @@ static int aio_read(struct kiocb *req, c + if (unlikely(!file->f_op->read_iter)) + return -EINVAL; + +- ret = aio_setup_rw(READ, iocb, &iovec, vectored, compat, &iter); ++ ret = aio_setup_rw(ITER_DEST, iocb, &iovec, vectored, compat, &iter); + if (ret < 0) + return ret; + ret = rw_verify_area(READ, file, &req->ki_pos, iov_iter_count(&iter)); +@@ -1575,7 +1575,7 @@ static int aio_write(struct kiocb *req, + if (unlikely(!file->f_op->write_iter)) + return -EINVAL; + +- ret = aio_setup_rw(WRITE, iocb, &iovec, vectored, compat, &iter); ++ ret = aio_setup_rw(ITER_SOURCE, iocb, &iovec, vectored, compat, &iter); + if (ret < 0) + return ret; + ret = rw_verify_area(WRITE, file, &req->ki_pos, iov_iter_count(&iter)); +--- a/fs/ceph/file.c ++++ b/fs/ceph/file.c +@@ -1115,7 +1115,7 @@ static void ceph_aio_complete_req(struct + aio_req->total_len = rc + zlen; + } + +- iov_iter_bvec(&i, READ, osd_data->bvec_pos.bvecs, ++ iov_iter_bvec(&i, ITER_DEST, osd_data->bvec_pos.bvecs, + osd_data->num_bvecs, + osd_data->bvec_pos.iter.bi_size); + iov_iter_advance(&i, rc); +@@ -1342,7 +1342,7 @@ ceph_direct_read_write(struct kiocb *ioc + int zlen = min_t(size_t, len - ret, + size - pos - ret); + +- iov_iter_bvec(&i, READ, bvecs, num_pages, len); ++ iov_iter_bvec(&i, ITER_DEST, bvecs, num_pages, len); + iov_iter_advance(&i, ret); + iov_iter_zero(zlen, &i); + ret += zlen; +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -747,7 +747,7 @@ cifs_read_from_socket(struct TCP_Server_ + { + struct msghdr smb_msg = {}; + struct kvec iov = {.iov_base = buf, .iov_len = to_read}; +- iov_iter_kvec(&smb_msg.msg_iter, READ, &iov, 1, to_read); ++ iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read); + + return cifs_readv_from_socket(server, &smb_msg); + } +@@ -762,7 +762,7 @@ cifs_discard_from_socket(struct TCP_Serv + * and cifs_readv_from_socket sets msg_control and msg_controllen + * so little to initialize in struct msghdr + */ +- iov_iter_discard(&smb_msg.msg_iter, READ, to_read); ++ iov_iter_discard(&smb_msg.msg_iter, ITER_DEST, to_read); + + return cifs_readv_from_socket(server, &smb_msg); + } +@@ -774,7 +774,7 @@ cifs_read_page_from_socket(struct TCP_Se + struct msghdr smb_msg = {}; + struct bio_vec bv = { + .bv_page = page, .bv_len = to_read, .bv_offset = page_offset}; +- iov_iter_bvec(&smb_msg.msg_iter, READ, &bv, 1, to_read); ++ iov_iter_bvec(&smb_msg.msg_iter, ITER_DEST, &bv, 1, to_read); + return cifs_readv_from_socket(server, &smb_msg); + } + +--- a/fs/cifs/file.c ++++ b/fs/cifs/file.c +@@ -3180,7 +3180,7 @@ static ssize_t __cifs_writev( + ctx->iter = *from; + ctx->len = len; + } else { +- rc = setup_aio_ctx_iter(ctx, from, WRITE); ++ rc = setup_aio_ctx_iter(ctx, from, ITER_SOURCE); + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; +@@ -3920,7 +3920,7 @@ static ssize_t __cifs_readv( + ctx->iter = *to; + ctx->len = len; + } else { +- rc = setup_aio_ctx_iter(ctx, to, READ); ++ rc = setup_aio_ctx_iter(ctx, to, ITER_DEST); + if (rc) { + kref_put(&ctx->refcount, cifs_aio_ctx_release); + return rc; +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -4720,13 +4720,13 @@ handle_read_data(struct TCP_Server_Info + return 0; + } + +- iov_iter_bvec(&iter, WRITE, bvec, npages, data_len); ++ iov_iter_bvec(&iter, ITER_SOURCE, bvec, npages, data_len); + } else if (buf_len >= data_offset + data_len) { + /* read response payload is in buf */ + WARN_ONCE(npages > 0, "read data can be either in buf or in pages"); + iov.iov_base = buf + data_offset; + iov.iov_len = data_len; +- iov_iter_kvec(&iter, WRITE, &iov, 1, data_len); ++ iov_iter_kvec(&iter, ITER_SOURCE, &iov, 1, data_len); + } else { + /* read response payload cannot be in both buf and pages */ + WARN_ONCE(1, "buf can not contain only a part of read data"); +--- a/fs/cifs/transport.c ++++ b/fs/cifs/transport.c +@@ -362,7 +362,7 @@ __smb_send_rqst(struct TCP_Server_Info * + .iov_base = &rfc1002_marker, + .iov_len = 4 + }; +- iov_iter_kvec(&smb_msg.msg_iter, WRITE, &hiov, 1, 4); ++ iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, &hiov, 1, 4); + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) + goto unmask; +@@ -383,7 +383,7 @@ __smb_send_rqst(struct TCP_Server_Info * + size += iov[i].iov_len; + } + +- iov_iter_kvec(&smb_msg.msg_iter, WRITE, iov, n_vec, size); ++ iov_iter_kvec(&smb_msg.msg_iter, ITER_SOURCE, iov, n_vec, size); + + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) +@@ -399,7 +399,7 @@ __smb_send_rqst(struct TCP_Server_Info * + rqst_page_get_length(&rqst[j], i, &bvec.bv_len, + &bvec.bv_offset); + +- iov_iter_bvec(&smb_msg.msg_iter, WRITE, ++ iov_iter_bvec(&smb_msg.msg_iter, ITER_SOURCE, + &bvec, 1, bvec.bv_len); + rc = smb_send_kvec(server, &smb_msg, &sent); + if (rc < 0) +--- a/fs/nfsd/vfs.c ++++ b/fs/nfsd/vfs.c +@@ -1043,7 +1043,7 @@ __be32 nfsd_readv(struct svc_rqst *rqstp + ssize_t host_err; + + trace_nfsd_read_vector(rqstp, fhp, offset, *count); +- iov_iter_kvec(&iter, READ, vec, vlen, *count); ++ iov_iter_kvec(&iter, ITER_DEST, vec, vlen, *count); + host_err = vfs_iter_read(file, &iter, &ppos, 0); + return nfsd_finish_read(rqstp, fhp, file, offset, count, eof, host_err); + } +@@ -1133,7 +1133,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, s + if (stable && !use_wgather) + flags |= RWF_SYNC; + +- iov_iter_kvec(&iter, WRITE, vec, vlen, *cnt); ++ iov_iter_kvec(&iter, ITER_SOURCE, vec, vlen, *cnt); + since = READ_ONCE(file->f_wb_err); + if (verf) + nfsd_copy_write_verifier(verf, nn); +--- a/fs/ocfs2/cluster/tcp.c ++++ b/fs/ocfs2/cluster/tcp.c +@@ -902,7 +902,7 @@ static int o2net_recv_tcp_msg(struct soc + { + struct kvec vec = { .iov_len = len, .iov_base = data, }; + struct msghdr msg = { .msg_flags = MSG_DONTWAIT, }; +- iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, len); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &vec, 1, len); + return sock_recvmsg(sock, &msg, MSG_DONTWAIT); + } + +--- a/fs/orangefs/inode.c ++++ b/fs/orangefs/inode.c +@@ -52,7 +52,7 @@ static int orangefs_writepage_locked(str + bv.bv_len = wlen; + bv.bv_offset = off % PAGE_SIZE; + WARN_ON(wlen == 0); +- iov_iter_bvec(&iter, WRITE, &bv, 1, wlen); ++ iov_iter_bvec(&iter, ITER_SOURCE, &bv, 1, wlen); + + ret = wait_for_direct_io(ORANGEFS_IO_WRITE, inode, &off, &iter, wlen, + len, wr, NULL, NULL); +@@ -110,7 +110,7 @@ static int orangefs_writepages_work(stru + else + ow->bv[i].bv_offset = 0; + } +- iov_iter_bvec(&iter, WRITE, ow->bv, ow->npages, ow->len); ++ iov_iter_bvec(&iter, ITER_SOURCE, ow->bv, ow->npages, ow->len); + + WARN_ON(ow->off >= len); + if (ow->off + ow->len > len) +@@ -275,7 +275,7 @@ static int orangefs_readpage(struct file + bv.bv_page = page; + bv.bv_len = PAGE_SIZE; + bv.bv_offset = 0; +- iov_iter_bvec(&iter, READ, &bv, 1, PAGE_SIZE); ++ iov_iter_bvec(&iter, ITER_DEST, &bv, 1, PAGE_SIZE); + + ret = wait_for_direct_io(ORANGEFS_IO_READ, inode, &off, &iter, + read_size, inode->i_size, NULL, &buffer_index, file); +--- a/fs/read_write.c ++++ b/fs/read_write.c +@@ -410,7 +410,7 @@ static ssize_t new_sync_read(struct file + + init_sync_kiocb(&kiocb, filp); + kiocb.ki_pos = (ppos ? *ppos : 0); +- iov_iter_init(&iter, READ, &iov, 1, len); ++ iov_iter_init(&iter, ITER_DEST, &iov, 1, len); + + ret = call_read_iter(filp, &kiocb, &iter); + BUG_ON(ret == -EIOCBQUEUED); +@@ -450,7 +450,7 @@ ssize_t __kernel_read(struct file *file, + + init_sync_kiocb(&kiocb, file); + kiocb.ki_pos = pos ? *pos : 0; +- iov_iter_kvec(&iter, READ, &iov, 1, iov.iov_len); ++ iov_iter_kvec(&iter, ITER_DEST, &iov, 1, iov.iov_len); + ret = file->f_op->read_iter(&kiocb, &iter); + if (ret > 0) { + if (pos) +@@ -513,7 +513,7 @@ static ssize_t new_sync_write(struct fil + + init_sync_kiocb(&kiocb, filp); + kiocb.ki_pos = (ppos ? *ppos : 0); +- iov_iter_init(&iter, WRITE, &iov, 1, len); ++ iov_iter_init(&iter, ITER_SOURCE, &iov, 1, len); + + ret = call_write_iter(filp, &kiocb, &iter); + BUG_ON(ret == -EIOCBQUEUED); +@@ -546,7 +546,7 @@ ssize_t __kernel_write(struct file *file + + init_sync_kiocb(&kiocb, file); + kiocb.ki_pos = pos ? *pos : 0; +- iov_iter_kvec(&iter, WRITE, &iov, 1, iov.iov_len); ++ iov_iter_kvec(&iter, ITER_SOURCE, &iov, 1, iov.iov_len); + ret = file->f_op->write_iter(&kiocb, &iter); + if (ret > 0) { + if (pos) +@@ -916,7 +916,7 @@ static ssize_t vfs_readv(struct file *fi + struct iov_iter iter; + ssize_t ret; + +- ret = import_iovec(READ, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter); ++ ret = import_iovec(ITER_DEST, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter); + if (ret >= 0) { + ret = do_iter_read(file, &iter, pos, flags); + kfree(iov); +@@ -933,7 +933,7 @@ static ssize_t vfs_writev(struct file *f + struct iov_iter iter; + ssize_t ret; + +- ret = import_iovec(WRITE, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter); ++ ret = import_iovec(ITER_SOURCE, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter); + if (ret >= 0) { + file_start_write(file); + ret = do_iter_write(file, &iter, pos, flags); +--- a/fs/seq_file.c ++++ b/fs/seq_file.c +@@ -156,7 +156,7 @@ ssize_t seq_read(struct file *file, char + ssize_t ret; + + init_sync_kiocb(&kiocb, file); +- iov_iter_init(&iter, READ, &iov, 1, size); ++ iov_iter_init(&iter, ITER_DEST, &iov, 1, size); + + kiocb.ki_pos = *ppos; + ret = seq_read_iter(&kiocb, &iter); +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -304,7 +304,7 @@ ssize_t generic_file_splice_read(struct + unsigned int i_head; + int ret; + +- iov_iter_pipe(&to, READ, pipe, len); ++ iov_iter_pipe(&to, ITER_DEST, pipe, len); + i_head = to.head; + init_sync_kiocb(&kiocb, in); + kiocb.ki_pos = *ppos; +@@ -685,7 +685,7 @@ iter_file_splice_write(struct pipe_inode + n++; + } + +- iov_iter_bvec(&from, WRITE, array, n, sd.total_len - left); ++ iov_iter_bvec(&from, ITER_SOURCE, array, n, sd.total_len - left); + ret = vfs_iter_write(out, &from, &sd.pos, 0); + if (ret <= 0) + break; +@@ -1263,9 +1263,9 @@ static int vmsplice_type(struct fd f, in + if (!f.file) + return -EBADF; + if (f.file->f_mode & FMODE_WRITE) { +- *type = WRITE; ++ *type = ITER_SOURCE; + } else if (f.file->f_mode & FMODE_READ) { +- *type = READ; ++ *type = ITER_DEST; + } else { + fdput(f); + return -EBADF; +@@ -1314,7 +1314,7 @@ SYSCALL_DEFINE4(vmsplice, int, fd, const + + if (!iov_iter_count(&iter)) + error = 0; +- else if (iov_iter_rw(&iter) == WRITE) ++ else if (type == ITER_SOURCE) + error = vmsplice_to_pipe(f.file, &iter, flags); + else + error = vmsplice_to_user(f.file, &iter, flags); +--- a/include/linux/uio.h ++++ b/include/linux/uio.h +@@ -26,6 +26,9 @@ enum iter_type { + ITER_DISCARD = 64, + }; + ++#define ITER_SOURCE 1 // == WRITE ++#define ITER_DEST 0 // == READ ++ + struct iov_iter_state { + size_t iov_offset; + size_t count; +--- a/mm/madvise.c ++++ b/mm/madvise.c +@@ -1184,7 +1184,7 @@ SYSCALL_DEFINE5(process_madvise, int, pi + goto out; + } + +- ret = import_iovec(READ, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter); ++ ret = import_iovec(ITER_DEST, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter); + if (ret < 0) + goto out; + +--- a/mm/page_io.c ++++ b/mm/page_io.c +@@ -269,7 +269,7 @@ int __swap_writepage(struct page *page, + }; + struct iov_iter from; + +- iov_iter_bvec(&from, WRITE, &bv, 1, PAGE_SIZE); ++ iov_iter_bvec(&from, ITER_SOURCE, &bv, 1, PAGE_SIZE); + init_sync_kiocb(&kiocb, swap_file); + kiocb.ki_pos = page_file_offset(page); + +--- a/mm/process_vm_access.c ++++ b/mm/process_vm_access.c +@@ -264,7 +264,7 @@ static ssize_t process_vm_rw(pid_t pid, + struct iovec *iov_r = iovstack_r; + struct iov_iter iter; + ssize_t rc; +- int dir = vm_write ? WRITE : READ; ++ int dir = vm_write ? ITER_SOURCE : ITER_DEST; + + if (flags != 0) + return -EINVAL; +--- a/net/9p/client.c ++++ b/net/9p/client.c +@@ -2095,7 +2095,7 @@ int p9_client_readdir(struct p9_fid *fid + struct kvec kv = {.iov_base = data, .iov_len = count}; + struct iov_iter to; + +- iov_iter_kvec(&to, READ, &kv, 1, count); ++ iov_iter_kvec(&to, ITER_DEST, &kv, 1, count); + + p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n", + fid->fid, (unsigned long long) offset, count); +--- a/net/bluetooth/6lowpan.c ++++ b/net/bluetooth/6lowpan.c +@@ -478,7 +478,7 @@ static int send_pkt(struct l2cap_chan *c + iv.iov_len = skb->len; + + memset(&msg, 0, sizeof(msg)); +- iov_iter_kvec(&msg.msg_iter, WRITE, &iv, 1, skb->len); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &iv, 1, skb->len); + + err = l2cap_chan_send(chan, &msg, skb->len); + if (err > 0) { +--- a/net/bluetooth/a2mp.c ++++ b/net/bluetooth/a2mp.c +@@ -56,7 +56,7 @@ static void a2mp_send(struct amp_mgr *mg + + memset(&msg, 0, sizeof(msg)); + +- iov_iter_kvec(&msg.msg_iter, WRITE, &iv, 1, total_len); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &iv, 1, total_len); + + l2cap_chan_send(chan, &msg, total_len); + +--- a/net/bluetooth/smp.c ++++ b/net/bluetooth/smp.c +@@ -606,7 +606,7 @@ static void smp_send_cmd(struct l2cap_co + + memset(&msg, 0, sizeof(msg)); + +- iov_iter_kvec(&msg.msg_iter, WRITE, iv, 2, 1 + len); ++ iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, iv, 2, 1 + len); + + l2cap_chan_send(chan, &msg, 1 + len); + +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -1812,7 +1812,7 @@ static int tcp_copy_straggler_data(struc + if (copy_address != zc->copybuf_address) + return -EINVAL; + +- err = import_single_range(READ, (void __user *)copy_address, ++ err = import_single_range(ITER_DEST, (void __user *)copy_address, + copylen, &iov, &msg.msg_iter); + if (err) + return err; +--- a/net/netfilter/ipvs/ip_vs_sync.c ++++ b/net/netfilter/ipvs/ip_vs_sync.c +@@ -1617,7 +1617,7 @@ ip_vs_receive(struct socket *sock, char + EnterFunction(7); + + /* Receive a packet */ +- iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, buflen); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, buflen); + len = sock_recvmsg(sock, &msg, MSG_DONTWAIT); + if (len < 0) + return len; +--- a/net/smc/smc_clc.c ++++ b/net/smc/smc_clc.c +@@ -361,7 +361,7 @@ int smc_clc_wait_msg(struct smc_sock *sm + */ + krflags = MSG_PEEK | MSG_WAITALL; + clc_sk->sk_rcvtimeo = timeout; +- iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &vec, 1, + sizeof(struct smc_clc_msg_hdr)); + len = sock_recvmsg(smc->clcsock, &msg, krflags); + if (signal_pending(current)) { +@@ -408,7 +408,7 @@ int smc_clc_wait_msg(struct smc_sock *sm + } else { + recvlen = datlen; + } +- iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, recvlen); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &vec, 1, recvlen); + krflags = MSG_WAITALL; + len = sock_recvmsg(smc->clcsock, &msg, krflags); + if (len < recvlen || !smc_clc_msg_hdr_valid(clcm, check_trl)) { +@@ -425,7 +425,7 @@ int smc_clc_wait_msg(struct smc_sock *sm + /* receive remaining proposal message */ + recvlen = datlen > SMC_CLC_RECV_BUF_LEN ? + SMC_CLC_RECV_BUF_LEN : datlen; +- iov_iter_kvec(&msg.msg_iter, READ, &vec, 1, recvlen); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &vec, 1, recvlen); + len = sock_recvmsg(smc->clcsock, &msg, krflags); + if (len < recvlen) { + smc->sk.sk_err = EPROTO; +--- a/net/socket.c ++++ b/net/socket.c +@@ -706,7 +706,7 @@ EXPORT_SYMBOL(sock_sendmsg); + int kernel_sendmsg(struct socket *sock, struct msghdr *msg, + struct kvec *vec, size_t num, size_t size) + { +- iov_iter_kvec(&msg->msg_iter, WRITE, vec, num, size); ++ iov_iter_kvec(&msg->msg_iter, ITER_SOURCE, vec, num, size); + return sock_sendmsg(sock, msg); + } + EXPORT_SYMBOL(kernel_sendmsg); +@@ -732,7 +732,7 @@ int kernel_sendmsg_locked(struct sock *s + if (!sock->ops->sendmsg_locked) + return sock_no_sendmsg_locked(sk, msg, size); + +- iov_iter_kvec(&msg->msg_iter, WRITE, vec, num, size); ++ iov_iter_kvec(&msg->msg_iter, ITER_SOURCE, vec, num, size); + + return sock->ops->sendmsg_locked(sk, msg, msg_data_left(msg)); + } +@@ -944,7 +944,7 @@ int kernel_recvmsg(struct socket *sock, + struct kvec *vec, size_t num, size_t size, int flags) + { + msg->msg_control_is_user = false; +- iov_iter_kvec(&msg->msg_iter, READ, vec, num, size); ++ iov_iter_kvec(&msg->msg_iter, ITER_DEST, vec, num, size); + return sock_recvmsg(sock, msg, flags); + } + EXPORT_SYMBOL(kernel_recvmsg); +@@ -1982,7 +1982,7 @@ int __sys_sendto(int fd, void __user *bu + struct iovec iov; + int fput_needed; + +- err = import_single_range(WRITE, buff, len, &iov, &msg.msg_iter); ++ err = import_single_range(ITER_SOURCE, buff, len, &iov, &msg.msg_iter); + if (unlikely(err)) + return err; + sock = sockfd_lookup_light(fd, &err, &fput_needed); +@@ -2043,7 +2043,7 @@ int __sys_recvfrom(int fd, void __user * + int err, err2; + int fput_needed; + +- err = import_single_range(READ, ubuf, size, &iov, &msg.msg_iter); ++ err = import_single_range(ITER_DEST, ubuf, size, &iov, &msg.msg_iter); + if (unlikely(err)) + return err; + sock = sockfd_lookup_light(fd, &err, &fput_needed); +@@ -2314,7 +2314,7 @@ static int copy_msghdr_from_user(struct + if (err) + return err; + +- err = import_iovec(save_addr ? READ : WRITE, ++ err = import_iovec(save_addr ? ITER_DEST : ITER_SOURCE, + msg.msg_iov, msg.msg_iovlen, + UIO_FASTIOV, iov, &kmsg->msg_iter); + return err < 0 ? err : 0; +--- a/net/sunrpc/socklib.c ++++ b/net/sunrpc/socklib.c +@@ -213,7 +213,7 @@ static inline int xprt_sendmsg(struct so + static int xprt_send_kvec(struct socket *sock, struct msghdr *msg, + struct kvec *vec, size_t seek) + { +- iov_iter_kvec(&msg->msg_iter, WRITE, vec, 1, vec->iov_len); ++ iov_iter_kvec(&msg->msg_iter, ITER_SOURCE, vec, 1, vec->iov_len); + return xprt_sendmsg(sock, msg, seek); + } + +@@ -226,7 +226,7 @@ static int xprt_send_pagedata(struct soc + if (err < 0) + return err; + +- iov_iter_bvec(&msg->msg_iter, WRITE, xdr->bvec, xdr_buf_pagecount(xdr), ++ iov_iter_bvec(&msg->msg_iter, ITER_SOURCE, xdr->bvec, xdr_buf_pagecount(xdr), + xdr->page_len + xdr->page_base); + return xprt_sendmsg(sock, msg, base + xdr->page_base); + } +@@ -249,7 +249,7 @@ static int xprt_send_rm_and_kvec(struct + }; + size_t len = iov[0].iov_len + iov[1].iov_len; + +- iov_iter_kvec(&msg->msg_iter, WRITE, iov, 2, len); ++ iov_iter_kvec(&msg->msg_iter, ITER_SOURCE, iov, 2, len); + return xprt_sendmsg(sock, msg, base); + } + +--- a/net/sunrpc/svcsock.c ++++ b/net/sunrpc/svcsock.c +@@ -271,7 +271,7 @@ static ssize_t svc_tcp_read_msg(struct s + rqstp->rq_respages = &rqstp->rq_pages[i]; + rqstp->rq_next_page = rqstp->rq_respages + 1; + +- iov_iter_bvec(&msg.msg_iter, READ, bvec, i, buflen); ++ iov_iter_bvec(&msg.msg_iter, ITER_DEST, bvec, i, buflen); + if (seek) { + iov_iter_advance(&msg.msg_iter, seek); + buflen -= seek; +@@ -880,7 +880,7 @@ static ssize_t svc_tcp_read_marker(struc + want = sizeof(rpc_fraghdr) - svsk->sk_tcplen; + iov.iov_base = ((char *)&svsk->sk_marker) + svsk->sk_tcplen; + iov.iov_len = want; +- iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, want); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, want); + len = sock_recvmsg(svsk->sk_sock, &msg, MSG_DONTWAIT); + if (len < 0) + return len; +--- a/net/sunrpc/xprtsock.c ++++ b/net/sunrpc/xprtsock.c +@@ -358,7 +358,7 @@ static ssize_t + xs_read_kvec(struct socket *sock, struct msghdr *msg, int flags, + struct kvec *kvec, size_t count, size_t seek) + { +- iov_iter_kvec(&msg->msg_iter, READ, kvec, 1, count); ++ iov_iter_kvec(&msg->msg_iter, ITER_DEST, kvec, 1, count); + return xs_sock_recvmsg(sock, msg, flags, seek); + } + +@@ -367,7 +367,7 @@ xs_read_bvec(struct socket *sock, struct + struct bio_vec *bvec, unsigned long nr, size_t count, + size_t seek) + { +- iov_iter_bvec(&msg->msg_iter, READ, bvec, nr, count); ++ iov_iter_bvec(&msg->msg_iter, ITER_DEST, bvec, nr, count); + return xs_sock_recvmsg(sock, msg, flags, seek); + } + +@@ -375,7 +375,7 @@ static ssize_t + xs_read_discard(struct socket *sock, struct msghdr *msg, int flags, + size_t count) + { +- iov_iter_discard(&msg->msg_iter, READ, count); ++ iov_iter_discard(&msg->msg_iter, ITER_DEST, count); + return sock_recvmsg(sock, msg, flags); + } + +--- a/net/tipc/topsrv.c ++++ b/net/tipc/topsrv.c +@@ -396,7 +396,7 @@ static int tipc_conn_rcv_from_sock(struc + iov.iov_base = &s; + iov.iov_len = sizeof(s); + msg.msg_name = NULL; +- iov_iter_kvec(&msg.msg_iter, READ, &iov, 1, iov.iov_len); ++ iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, iov.iov_len); + ret = sock_recvmsg(con->sock, &msg, MSG_DONTWAIT); + if (ret == -EWOULDBLOCK) + return -EWOULDBLOCK; +--- a/net/tls/tls_device.c ++++ b/net/tls/tls_device.c +@@ -585,7 +585,7 @@ int tls_device_sendpage(struct sock *sk, + kaddr = kmap(page); + iov.iov_base = kaddr + offset; + iov.iov_len = size; +- iov_iter_kvec(&msg_iter, WRITE, &iov, 1, size); ++ iov_iter_kvec(&msg_iter, ITER_SOURCE, &iov, 1, size); + rc = tls_push_data(sk, &msg_iter, size, + flags, TLS_RECORD_TYPE_DATA); + kunmap(page); +@@ -660,7 +660,7 @@ static int tls_device_push_pending_recor + { + struct iov_iter msg_iter; + +- iov_iter_kvec(&msg_iter, WRITE, NULL, 0, 0); ++ iov_iter_kvec(&msg_iter, ITER_SOURCE, NULL, 0, 0); + return tls_push_data(sk, &msg_iter, 0, flags, TLS_RECORD_TYPE_DATA); + } + +--- a/net/xfrm/espintcp.c ++++ b/net/xfrm/espintcp.c +@@ -360,7 +360,7 @@ static int espintcp_sendmsg(struct sock + *((__be16 *)buf) = cpu_to_be16(msglen); + pfx_iov.iov_base = buf; + pfx_iov.iov_len = sizeof(buf); +- iov_iter_kvec(&pfx_iter, WRITE, &pfx_iov, 1, pfx_iov.iov_len); ++ iov_iter_kvec(&pfx_iter, ITER_SOURCE, &pfx_iov, 1, pfx_iov.iov_len); + + err = sk_msg_memcopy_from_iter(sk, &pfx_iter, &emsg->skmsg, + pfx_iov.iov_len); +--- a/security/keys/keyctl.c ++++ b/security/keys/keyctl.c +@@ -1256,7 +1256,7 @@ long keyctl_instantiate_key(key_serial_t + struct iov_iter from; + int ret; + +- ret = import_single_range(WRITE, (void __user *)_payload, plen, ++ ret = import_single_range(ITER_SOURCE, (void __user *)_payload, plen, + &iov, &from); + if (unlikely(ret)) + return ret; +@@ -1288,7 +1288,7 @@ long keyctl_instantiate_key_iov(key_seri + if (!_payload_iov) + ioc = 0; + +- ret = import_iovec(WRITE, _payload_iov, ioc, ++ ret = import_iovec(ITER_SOURCE, _payload_iov, ioc, + ARRAY_SIZE(iovstack), &iov, &from); + if (ret < 0) + return ret; diff --git a/queue-5.10/wifi-brcmfmac-fix-potential-use-after-free-issue-when-stopping-watchdog-task.patch b/queue-5.10/wifi-brcmfmac-fix-potential-use-after-free-issue-when-stopping-watchdog-task.patch new file mode 100644 index 0000000000..1dcc8fa189 --- /dev/null +++ b/queue-5.10/wifi-brcmfmac-fix-potential-use-after-free-issue-when-stopping-watchdog-task.patch @@ -0,0 +1,55 @@ +From stable+bounces-246865-greg=kroah.com@vger.kernel.org Wed May 13 19:27:28 2026 +From: Sasha Levin +Date: Wed, 13 May 2026 09:33:00 -0400 +Subject: wifi: brcmfmac: Fix potential use-after-free issue when stopping watchdog task +To: stable@vger.kernel.org +Cc: Marek Szyprowski , Arend van Spriel , Johannes Berg , Sasha Levin +Message-ID: <20260513133300.3732727-1-sashal@kernel.org> + +From: Marek Szyprowski + +[ Upstream commit c623b63580880cc742255eaed3d79804c1b91143 ] + +Watchdog task might end between send_sig() and kthread_stop() calls, what +results in the use-after-free issue. Fix this by increasing watchdog task +reference count before calling send_sig() and dropping it by switching to +kthread_stop_put(). + +Cc: stable@vger.kernel.org +Fixes: 373c83a801f1 ("brcmfmac: stop watchdog before detach and free everything") +Fixes: a9ffda88be74 ("brcm80211: fmac: abstract bus_stop interface function pointer") +Signed-off-by: Marek Szyprowski +Acked-by: Arend van Spriel +Link: https://patch.msgid.link/20260416093339.2066829-1-m.szyprowski@samsung.com +Signed-off-by: Johannes Berg +[ replaced kthread_stop_put() with open-coded kthread_stop() + put_task_struct() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +@@ -2464,8 +2464,10 @@ static void brcmf_sdio_bus_stop(struct d + brcmf_dbg(TRACE, "Enter\n"); + + if (bus->watchdog_tsk) { ++ get_task_struct(bus->watchdog_tsk); + send_sig(SIGTERM, bus->watchdog_tsk, 1); + kthread_stop(bus->watchdog_tsk); ++ put_task_struct(bus->watchdog_tsk); + bus->watchdog_tsk = NULL; + } + +@@ -4536,8 +4538,10 @@ void brcmf_sdio_remove(struct brcmf_sdio + if (bus) { + /* Stop watchdog task */ + if (bus->watchdog_tsk) { ++ get_task_struct(bus->watchdog_tsk); + send_sig(SIGTERM, bus->watchdog_tsk, 1); + kthread_stop(bus->watchdog_tsk); ++ put_task_struct(bus->watchdog_tsk); + bus->watchdog_tsk = NULL; + } + diff --git a/queue-5.10/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch b/queue-5.10/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch new file mode 100644 index 0000000000..dfb0d020d8 --- /dev/null +++ b/queue-5.10/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch @@ -0,0 +1,49 @@ +From stable+bounces-242198-greg=kroah.com@vger.kernel.org Fri May 1 01:17:06 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 15:46:57 -0400 +Subject: wifi: mwifiex: fix use-after-free in mwifiex_adapter_cleanup() +To: stable@vger.kernel.org +Cc: Daniel Hodges , Johannes Berg , Sasha Levin +Message-ID: <20260430194657.1996779-1-sashal@kernel.org> + +From: Daniel Hodges + +[ Upstream commit ae5e95d4157481693be2317e3ffcd84e36010cbb ] + +The mwifiex_adapter_cleanup() function uses timer_delete() +(non-synchronous) for the wakeup_timer before the adapter structure is +freed. This is incorrect because timer_delete() does not wait for any +running timer callback to complete. + +If the wakeup_timer callback (wakeup_timer_fn) is executing when +mwifiex_adapter_cleanup() is called, the callback will continue to +access adapter fields (adapter->hw_status, adapter->if_ops.card_reset, +etc.) which may be freed by mwifiex_free_adapter() called later in the +mwifiex_remove_card() path. + +Use timer_delete_sync() instead to ensure any running timer callback has +completed before returning. + +Fixes: 4636187da60b ("mwifiex: add wakeup timer based recovery mechanism") +Cc: stable@vger.kernel.org +Signed-off-by: Daniel Hodges +Link: https://patch.msgid.link/20260206194401.2346-1-git@danielhodges.dev +Signed-off-by: Johannes Berg +[ changed `timer_delete_sync(&adapter->wakeup_timer)` to `del_timer_sync(&adapter->wakeup_timer)` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/marvell/mwifiex/init.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/marvell/mwifiex/init.c ++++ b/drivers/net/wireless/marvell/mwifiex/init.c +@@ -399,7 +399,7 @@ static void mwifiex_invalidate_lists(str + static void + mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) + { +- del_timer(&adapter->wakeup_timer); ++ del_timer_sync(&adapter->wakeup_timer); + del_timer_sync(&adapter->devdump_timer); + mwifiex_cancel_all_pending_cmd(adapter); + wake_up_interruptible(&adapter->cmd_wait_q.wait);