From: Greg Kroah-Hartman Date: Tue, 16 Jun 2026 05:32:02 +0000 (+0530) Subject: 5.15-stable patches X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=261fa49f894b594fcbf6fa7788bf3070b0692998;p=thirdparty%2Fkernel%2Fstable-queue.git 5.15-stable patches added patches: 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 bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.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 crypto-nx-avoid-wflex-array-member-not-at-end-warning.patch crypto-nx-fix-bounce-buffer-leaks-in-nx842_crypto_-alloc-free-_ctx.patch crypto-nx-migrate-to-scomp-api.patch erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch erofs-fix-unsigned-underflow-in-z_erofs_lz4_handle_overlap.patch f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch io_uring-poll-fix-signed-comparison-in-io_poll_get_ownership.patch ksmbd-compare-macs-in-constant-time.patch ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch lib-crypto-mpi-fix-integer-underflow-in-mpi_read_raw_from_sgl.patch media-rc-igorplugusb-heed-coherency-rules.patch media-rc-ttusbir-respect-dma-coherency-rules.patch mmc-sdhci-of-dwcmshc-disable-clock-before-dll-configuration.patch mtd-docg3-convert-to-platform-remove-callback-returning-void.patch mtd-docg3-fix-use-after-free-in-docg3_release.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-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.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-tcp-md5-fix-mac-comparison-to-be-constant-time.patch nvme-fix-interpretation-of-dmrsl.patch nvme-respect-nvme_quirk_disable_write_zeroes-when-wzsl-is-set.patch printk-add-print_hex_dump_devel.patch randomize_kstack-maintain-kstack_offset-per-task.patch rtw88-8821ce-disable-pcie-aspm-l1-for-8821ce-using-chip-id.patch rxrpc-fix-conn-level-packet-handling-to-unshare-response-packets.patch sched-use-u64-for-bandwidth-ratio-calculations.patch scsi-core-pm-rely-on-the-device-driver-core-for-async-power-management.patch scsi-sd-add-error-handling-support-for-add_disk.patch scsi-sd-fix-missing-put_disk-when-device_add-disk_dev-fails.patch sd-rename-the-scsi_disk.dev-field.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-server-fix-active_num_conn-leak-on-transport-allocation-failure.patch smb-server-fix-max_connections-off-by-one-in-tcp-accept-path.patch thermal-core-fix-thermal-zone-governor-cleanup-issues.patch tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch udf-fix-partition-descriptor-append-bookkeeping.patch wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch wifi-rtw88-check-for-pci-upstream-bridge-existence.patch --- diff --git a/queue-5.15/alsa-aloop-fix-peer-runtime-uaf-during-format-change-stop.patch b/queue-5.15/alsa-aloop-fix-peer-runtime-uaf-during-format-change-stop.patch new file mode 100644 index 0000000000..7f3e7b235a --- /dev/null +++ b/queue-5.15/alsa-aloop-fix-peer-runtime-uaf-during-format-change-stop.patch @@ -0,0 +1,119 @@ +From stable+bounces-245017-greg=kroah.com@vger.kernel.org Sun May 10 18:46:05 2026 +From: Sasha Levin +Date: Sun, 10 May 2026 09:15:57 -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: <20260510131557.4115089-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 +@@ -98,6 +98,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; +@@ -341,8 +344,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; +@@ -994,24 +1001,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) +@@ -1206,6 +1218,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.15/alsa-aoa-i2sbus-clear-stale-prepared-state.patch b/queue-5.15/alsa-aoa-i2sbus-clear-stale-prepared-state.patch new file mode 100644 index 0000000000..ada4224aa2 --- /dev/null +++ b/queue-5.15/alsa-aoa-i2sbus-clear-stale-prepared-state.patch @@ -0,0 +1,165 @@ +From stable+bounces-242473-greg=kroah.com@vger.kernel.org Fri May 1 22:47:13 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 13:13:18 -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: <20260501171318.3681281-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.15/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch b/queue-5.15/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch new file mode 100644 index 0000000000..cabd312da3 --- /dev/null +++ b/queue-5.15/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch @@ -0,0 +1,83 @@ +From stable+bounces-242487-greg=kroah.com@vger.kernel.org Sat May 2 00:06:49 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 14:35:58 -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: <20260501183559.3910873-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.15/alsa-aoa-use-guard-for-mutex-locks.patch b/queue-5.15/alsa-aoa-use-guard-for-mutex-locks.patch new file mode 100644 index 0000000000..9e76132270 --- /dev/null +++ b/queue-5.15/alsa-aoa-use-guard-for-mutex-locks.patch @@ -0,0 +1,1041 @@ +From stable+bounces-242472-greg=kroah.com@vger.kernel.org Fri May 1 22:47:11 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 13:13:17 -0400 +Subject: ALSA: aoa: Use guard() for mutex locks +To: stable@vger.kernel.org +Cc: Takashi Iwai , Sasha Levin +Message-ID: <20260501171318.3681281-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 +@@ -235,10 +235,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; + } + +@@ -254,18 +253,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; + } + +@@ -285,10 +281,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; + } + +@@ -297,18 +292,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; + } + +@@ -337,10 +329,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; + } +@@ -351,19 +342,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; + } + +@@ -396,9 +384,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; + } + +@@ -411,16 +398,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; + } + +@@ -440,9 +424,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; + } + +@@ -451,16 +434,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; + } + +@@ -486,9 +466,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; + } + +@@ -500,7 +479,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; + + /* +@@ -512,13 +491,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; + } + +@@ -557,9 +533,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; + } + +@@ -571,16 +546,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; + } + +@@ -608,9 +580,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; + } + +@@ -622,16 +593,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; + } + +@@ -722,13 +690,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 */ +@@ -743,23 +711,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; + } + +@@ -802,14 +768,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.15/alsa-core-fix-potential-data-race-at-fasync-handling.patch b/queue-5.15/alsa-core-fix-potential-data-race-at-fasync-handling.patch new file mode 100644 index 0000000000..1c9ed76009 --- /dev/null +++ b/queue-5.15/alsa-core-fix-potential-data-race-at-fasync-handling.patch @@ -0,0 +1,76 @@ +From stable+bounces-242652-greg=kroah.com@vger.kernel.org Sun May 3 16:28:28 2026 +From: Sasha Levin +Date: Sun, 3 May 2026 06:58:21 -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: <20260503105821.1029617-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.15/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch b/queue-5.15/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch new file mode 100644 index 0000000000..02c16b636e --- /dev/null +++ b/queue-5.15/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch @@ -0,0 +1,137 @@ +From stable+bounces-241760-greg=kroah.com@vger.kernel.org Wed Apr 29 00:24:42 2026 +From: Sasha Levin +Date: Tue, 28 Apr 2026 14:53:11 -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: <20260428185311.3151505-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 +[ replaced `__pte_clear()` with `pte_clear()` ] +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 +@@ -886,10 +886,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); + } + +@@ -910,15 +914,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_cont(pmd)); ++ 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)); +@@ -943,15 +946,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)); +@@ -981,6 +981,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; + +@@ -1002,6 +1003,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.15/bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.patch b/queue-5.15/bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.patch new file mode 100644 index 0000000000..08c7a829a5 --- /dev/null +++ b/queue-5.15/bluetooth-hci_event-fix-potential-uaf-in-ssp-passkey-handlers.patch @@ -0,0 +1,94 @@ +From stable+bounces-244878-greg=kroah.com@vger.kernel.org Sat May 9 07:03:27 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 21:33:20 -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: <20260509013320.2851098-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 +@@ -4953,9 +4953,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; +@@ -4964,6 +4966,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) +@@ -4973,14 +4978,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++; +@@ -4995,13 +5002,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.15/can-ucan-fix-devres-lifetime.patch b/queue-5.15/can-ucan-fix-devres-lifetime.patch new file mode 100644 index 0000000000..61e06ac789 --- /dev/null +++ b/queue-5.15/can-ucan-fix-devres-lifetime.patch @@ -0,0 +1,44 @@ +From stable+bounces-244882-greg=kroah.com@vger.kernel.org Sat May 9 07:35:52 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 22:04:53 -0400 +Subject: can: ucan: fix devres lifetime +To: stable@vger.kernel.org +Cc: Johan Hovold , Jakob Unterwurzacher , Marc Kleine-Budde , Sasha Levin +Message-ID: <20260509020453.2868235-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 +@@ -1396,7 +1396,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.15/can-ucan-fix-typos-in-comments.patch b/queue-5.15/can-ucan-fix-typos-in-comments.patch new file mode 100644 index 0000000000..1881167bff --- /dev/null +++ b/queue-5.15/can-ucan-fix-typos-in-comments.patch @@ -0,0 +1,46 @@ +From stable+bounces-244881-greg=kroah.com@vger.kernel.org Sat May 9 07:35:01 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 22:04:52 -0400 +Subject: can: ucan: fix typos in comments +To: stable@vger.kernel.org +Cc: Julia Lawall , Marc Kleine-Budde , Sasha Levin +Message-ID: <20260509020453.2868235-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 +@@ -1395,7 +1395,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); +@@ -1529,7 +1529,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.15/ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch b/queue-5.15/ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch new file mode 100644 index 0000000000..2e7910e36e --- /dev/null +++ b/queue-5.15/ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch @@ -0,0 +1,113 @@ +From stable+bounces-244965-greg=kroah.com@vger.kernel.org Sat May 9 20:52:42 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 11:22:35 -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: <20260509152235.3512498-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 +@@ -721,7 +721,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); + } + } + } +@@ -777,7 +778,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.15/crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch b/queue-5.15/crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch new file mode 100644 index 0000000000..518e37e441 --- /dev/null +++ b/queue-5.15/crypto-caam-guard-hmac-key-hex-dumps-in-hash_digest_key.patch @@ -0,0 +1,68 @@ +From stable+bounces-245031-greg=kroah.com@vger.kernel.org Sun May 10 20:40:20 2026 +From: Sasha Levin +Date: Sun, 10 May 2026 11:10:10 -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: <20260510151010.38344-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 +@@ -3264,7 +3264,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), +@@ -3284,7 +3284,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.15/crypto-nx-avoid-wflex-array-member-not-at-end-warning.patch b/queue-5.15/crypto-nx-avoid-wflex-array-member-not-at-end-warning.patch new file mode 100644 index 0000000000..9f3084b45d --- /dev/null +++ b/queue-5.15/crypto-nx-avoid-wflex-array-member-not-at-end-warning.patch @@ -0,0 +1,120 @@ +From stable+bounces-244907-greg=kroah.com@vger.kernel.org Sat May 9 09:23:40 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 23:53:31 -0400 +Subject: crypto: nx - Avoid -Wflex-array-member-not-at-end warning +To: stable@vger.kernel.org +Cc: "Gustavo A. R. Silva" , Herbert Xu , Sasha Levin +Message-ID: <20260509035333.3119717-1-sashal@kernel.org> + +From: "Gustavo A. R. Silva" + +[ Upstream commit 1e6b251ce1759392666856908113dd5d7cea044d ] + +-Wflex-array-member-not-at-end is coming in GCC-14, and we are getting +ready to enable it globally. So, we are deprecating flexible-array +members in the middle of another structure. + +There is currently an object (`header`) in `struct nx842_crypto_ctx` +that contains a flexible structure (`struct nx842_crypto_header`): + +struct nx842_crypto_ctx { + ... + struct nx842_crypto_header header; + struct nx842_crypto_header_group group[NX842_CRYPTO_GROUP_MAX]; + ... +}; + +So, in order to avoid ending up with a flexible-array member in the +middle of another struct, we use the `struct_group_tagged()` helper to +separate the flexible array from the rest of the members in the flexible +structure: + +struct nx842_crypto_header { + struct_group_tagged(nx842_crypto_header_hdr, hdr, + + ... the rest of the members + + ); + struct nx842_crypto_header_group group[]; +} __packed; + +With the change described above, we can now declare an object of the +type of the tagged struct, without embedding the flexible array in the +middle of another struct: + +struct nx842_crypto_ctx { + ... + struct nx842_crypto_header_hdr header; + struct nx842_crypto_header_group group[NX842_CRYPTO_GROUP_MAX]; + ... + } __packed; + +We also use `container_of()` whenever we need to retrieve a pointer to +the flexible structure, through which we can access the flexible +array if needed. + +So, with these changes, fix the following warning: + +In file included from drivers/crypto/nx/nx-842.c:55: +drivers/crypto/nx/nx-842.h:174:36: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end] + 174 | struct nx842_crypto_header header; + | ^~~~~~ + +Signed-off-by: Gustavo A. R. Silva +Signed-off-by: Herbert Xu +Stable-dep-of: adb3faf2db1a ("crypto: nx - fix bounce buffer leaks in nx842_crypto_{alloc,free}_ctx") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/nx/nx-842.c | 6 ++++-- + drivers/crypto/nx/nx-842.h | 10 ++++++---- + 2 files changed, 10 insertions(+), 6 deletions(-) + +--- a/drivers/crypto/nx/nx-842.c ++++ b/drivers/crypto/nx/nx-842.c +@@ -251,7 +251,9 @@ int nx842_crypto_compress(struct crypto_ + u8 *dst, unsigned int *dlen) + { + struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm); +- struct nx842_crypto_header *hdr = &ctx->header; ++ struct nx842_crypto_header *hdr = ++ container_of(&ctx->header, ++ struct nx842_crypto_header, hdr); + struct nx842_crypto_param p; + struct nx842_constraints c = *ctx->driver->constraints; + unsigned int groups, hdrsize, h; +@@ -490,7 +492,7 @@ int nx842_crypto_decompress(struct crypt + } + + memcpy(&ctx->header, src, hdr_len); +- hdr = &ctx->header; ++ hdr = container_of(&ctx->header, struct nx842_crypto_header, hdr); + + for (n = 0; n < hdr->groups; n++) { + /* ignore applies to last group */ +--- a/drivers/crypto/nx/nx-842.h ++++ b/drivers/crypto/nx/nx-842.h +@@ -157,9 +157,11 @@ struct nx842_crypto_header_group { + } __packed; + + struct nx842_crypto_header { +- __be16 magic; /* NX842_CRYPTO_MAGIC */ +- __be16 ignore; /* decompressed end bytes to ignore */ +- u8 groups; /* total groups in this header */ ++ struct_group_tagged(nx842_crypto_header_hdr, hdr, ++ __be16 magic; /* NX842_CRYPTO_MAGIC */ ++ __be16 ignore; /* decompressed end bytes to ignore */ ++ u8 groups; /* total groups in this header */ ++ ); + struct nx842_crypto_header_group group[]; + } __packed; + +@@ -171,7 +173,7 @@ struct nx842_crypto_ctx { + u8 *wmem; + u8 *sbounce, *dbounce; + +- struct nx842_crypto_header header; ++ struct nx842_crypto_header_hdr header; + struct nx842_crypto_header_group group[NX842_CRYPTO_GROUP_MAX]; + + struct nx842_driver *driver; diff --git a/queue-5.15/crypto-nx-fix-bounce-buffer-leaks-in-nx842_crypto_-alloc-free-_ctx.patch b/queue-5.15/crypto-nx-fix-bounce-buffer-leaks-in-nx842_crypto_-alloc-free-_ctx.patch new file mode 100644 index 0000000000..8b358c2d1c --- /dev/null +++ b/queue-5.15/crypto-nx-fix-bounce-buffer-leaks-in-nx842_crypto_-alloc-free-_ctx.patch @@ -0,0 +1,51 @@ +From stable+bounces-244909-greg=kroah.com@vger.kernel.org Sat May 9 09:23:52 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 23:53:33 -0400 +Subject: crypto: nx - fix bounce buffer leaks in nx842_crypto_{alloc,free}_ctx +To: stable@vger.kernel.org +Cc: Thorsten Blum , Herbert Xu , Sasha Levin +Message-ID: <20260509035333.3119717-3-sashal@kernel.org> + +From: Thorsten Blum + +[ Upstream commit adb3faf2db1a66d0f015b44ac909a32dfc7f2f9c ] + +The bounce buffers are allocated with __get_free_pages() using +BOUNCE_BUFFER_ORDER (order 2 = 4 pages), but both the allocation error +path and nx842_crypto_free_ctx() release the buffers with free_page(). +Use free_pages() with the matching order instead. + +Fixes: ed70b479c2c0 ("crypto: nx - add hardware 842 crypto comp alg") +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/nx/nx-842.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/drivers/crypto/nx/nx-842.c ++++ b/drivers/crypto/nx/nx-842.c +@@ -116,8 +116,8 @@ void *nx842_crypto_alloc_ctx(struct nx84 + ctx->dbounce = (u8 *)__get_free_pages(GFP_KERNEL, BOUNCE_BUFFER_ORDER); + if (!ctx->wmem || !ctx->sbounce || !ctx->dbounce) { + kfree(ctx->wmem); +- free_page((unsigned long)ctx->sbounce); +- free_page((unsigned long)ctx->dbounce); ++ free_pages((unsigned long)ctx->sbounce, BOUNCE_BUFFER_ORDER); ++ free_pages((unsigned long)ctx->dbounce, BOUNCE_BUFFER_ORDER); + kfree(ctx); + return ERR_PTR(-ENOMEM); + } +@@ -131,8 +131,8 @@ void nx842_crypto_free_ctx(void *p) + struct nx842_crypto_ctx *ctx = p; + + kfree(ctx->wmem); +- free_page((unsigned long)ctx->sbounce); +- free_page((unsigned long)ctx->dbounce); ++ free_pages((unsigned long)ctx->sbounce, BOUNCE_BUFFER_ORDER); ++ free_pages((unsigned long)ctx->dbounce, BOUNCE_BUFFER_ORDER); + } + EXPORT_SYMBOL_GPL(nx842_crypto_free_ctx); + diff --git a/queue-5.15/crypto-nx-migrate-to-scomp-api.patch b/queue-5.15/crypto-nx-migrate-to-scomp-api.patch new file mode 100644 index 0000000000..325dda089b --- /dev/null +++ b/queue-5.15/crypto-nx-migrate-to-scomp-api.patch @@ -0,0 +1,279 @@ +From stable+bounces-244908-greg=kroah.com@vger.kernel.org Sat May 9 09:23:45 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 23:53:32 -0400 +Subject: crypto: nx - Migrate to scomp API +To: stable@vger.kernel.org +Cc: Ard Biesheuvel , Herbert Xu , Sasha Levin +Message-ID: <20260509035333.3119717-2-sashal@kernel.org> + +From: Ard Biesheuvel + +[ Upstream commit 980b5705f4e73f567e405cd18337cc32fd51cf79 ] + +The only remaining user of 842 compression has been migrated to the +acomp compression API, and so the NX hardware driver has to follow suit, +given that no users of the obsolete 'comp' API remain, and it is going +to be removed. + +So migrate the NX driver code to scomp. These will be wrapped and +exposed as acomp implementation via the crypto subsystem's +acomp-to-scomp adaptation layer. + +Signed-off-by: Ard Biesheuvel +Signed-off-by: Herbert Xu +Stable-dep-of: adb3faf2db1a ("crypto: nx - fix bounce buffer leaks in nx842_crypto_{alloc,free}_ctx") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/crypto/nx/nx-842.c | 33 +++++++++++++++++++-------------- + drivers/crypto/nx/nx-842.h | 14 ++++++++------ + drivers/crypto/nx/nx-common-powernv.c | 31 +++++++++++++++---------------- + drivers/crypto/nx/nx-common-pseries.c | 33 ++++++++++++++++----------------- + 4 files changed, 58 insertions(+), 53 deletions(-) + +--- a/drivers/crypto/nx/nx-842.c ++++ b/drivers/crypto/nx/nx-842.c +@@ -101,9 +101,13 @@ static int update_param(struct nx842_cry + return 0; + } + +-int nx842_crypto_init(struct crypto_tfm *tfm, struct nx842_driver *driver) ++void *nx842_crypto_alloc_ctx(struct nx842_driver *driver) + { +- struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct nx842_crypto_ctx *ctx; ++ ++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return ERR_PTR(-ENOMEM); + + spin_lock_init(&ctx->lock); + ctx->driver = driver; +@@ -114,22 +118,23 @@ int nx842_crypto_init(struct crypto_tfm + kfree(ctx->wmem); + free_page((unsigned long)ctx->sbounce); + free_page((unsigned long)ctx->dbounce); +- return -ENOMEM; ++ kfree(ctx); ++ return ERR_PTR(-ENOMEM); + } + +- return 0; ++ return ctx; + } +-EXPORT_SYMBOL_GPL(nx842_crypto_init); ++EXPORT_SYMBOL_GPL(nx842_crypto_alloc_ctx); + +-void nx842_crypto_exit(struct crypto_tfm *tfm) ++void nx842_crypto_free_ctx(void *p) + { +- struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct nx842_crypto_ctx *ctx = p; + + kfree(ctx->wmem); + free_page((unsigned long)ctx->sbounce); + free_page((unsigned long)ctx->dbounce); + } +-EXPORT_SYMBOL_GPL(nx842_crypto_exit); ++EXPORT_SYMBOL_GPL(nx842_crypto_free_ctx); + + static void check_constraints(struct nx842_constraints *c) + { +@@ -246,11 +251,11 @@ nospc: + return update_param(p, slen, dskip + dlen); + } + +-int nx842_crypto_compress(struct crypto_tfm *tfm, ++int nx842_crypto_compress(struct crypto_scomp *tfm, + const u8 *src, unsigned int slen, +- u8 *dst, unsigned int *dlen) ++ u8 *dst, unsigned int *dlen, void *pctx) + { +- struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct nx842_crypto_ctx *ctx = pctx; + struct nx842_crypto_header *hdr = + container_of(&ctx->header, + struct nx842_crypto_header, hdr); +@@ -431,11 +436,11 @@ usesw: + return update_param(p, slen + padding, dlen); + } + +-int nx842_crypto_decompress(struct crypto_tfm *tfm, ++int nx842_crypto_decompress(struct crypto_scomp *tfm, + const u8 *src, unsigned int slen, +- u8 *dst, unsigned int *dlen) ++ u8 *dst, unsigned int *dlen, void *pctx) + { +- struct nx842_crypto_ctx *ctx = crypto_tfm_ctx(tfm); ++ struct nx842_crypto_ctx *ctx = pctx; + struct nx842_crypto_header *hdr; + struct nx842_crypto_param p; + struct nx842_constraints c = *ctx->driver->constraints; +--- a/drivers/crypto/nx/nx-842.h ++++ b/drivers/crypto/nx/nx-842.h +@@ -101,6 +101,8 @@ + #define LEN_ON_SIZE(pa, size) ((size) - ((pa) & ((size) - 1))) + #define LEN_ON_PAGE(pa) LEN_ON_SIZE(pa, PAGE_SIZE) + ++struct crypto_scomp; ++ + static inline unsigned long nx842_get_pa(void *addr) + { + if (!is_vmalloc_addr(addr)) +@@ -179,13 +181,13 @@ struct nx842_crypto_ctx { + struct nx842_driver *driver; + }; + +-int nx842_crypto_init(struct crypto_tfm *tfm, struct nx842_driver *driver); +-void nx842_crypto_exit(struct crypto_tfm *tfm); +-int nx842_crypto_compress(struct crypto_tfm *tfm, ++void *nx842_crypto_alloc_ctx(struct nx842_driver *driver); ++void nx842_crypto_free_ctx(void *ctx); ++int nx842_crypto_compress(struct crypto_scomp *tfm, + const u8 *src, unsigned int slen, +- u8 *dst, unsigned int *dlen); +-int nx842_crypto_decompress(struct crypto_tfm *tfm, ++ u8 *dst, unsigned int *dlen, void *ctx); ++int nx842_crypto_decompress(struct crypto_scomp *tfm, + const u8 *src, unsigned int slen, +- u8 *dst, unsigned int *dlen); ++ u8 *dst, unsigned int *dlen, void *ctx); + + #endif /* __NX_842_H__ */ +--- a/drivers/crypto/nx/nx-common-powernv.c ++++ b/drivers/crypto/nx/nx-common-powernv.c +@@ -9,6 +9,7 @@ + + #include "nx-842.h" + ++#include + #include + + #include +@@ -1034,23 +1035,21 @@ static struct nx842_driver nx842_powernv + .decompress = nx842_powernv_decompress, + }; + +-static int nx842_powernv_crypto_init(struct crypto_tfm *tfm) ++static void *nx842_powernv_crypto_alloc_ctx(void) + { +- return nx842_crypto_init(tfm, &nx842_powernv_driver); ++ return nx842_crypto_alloc_ctx(&nx842_powernv_driver); + } + +-static struct crypto_alg nx842_powernv_alg = { +- .cra_name = "842", +- .cra_driver_name = "842-nx", +- .cra_priority = 300, +- .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, +- .cra_ctxsize = sizeof(struct nx842_crypto_ctx), +- .cra_module = THIS_MODULE, +- .cra_init = nx842_powernv_crypto_init, +- .cra_exit = nx842_crypto_exit, +- .cra_u = { .compress = { +- .coa_compress = nx842_crypto_compress, +- .coa_decompress = nx842_crypto_decompress } } ++static struct scomp_alg nx842_powernv_alg = { ++ .base.cra_name = "842", ++ .base.cra_driver_name = "842-nx", ++ .base.cra_priority = 300, ++ .base.cra_module = THIS_MODULE, ++ ++ .alloc_ctx = nx842_powernv_crypto_alloc_ctx, ++ .free_ctx = nx842_crypto_free_ctx, ++ .compress = nx842_crypto_compress, ++ .decompress = nx842_crypto_decompress, + }; + + static __init int nx_compress_powernv_init(void) +@@ -1110,7 +1109,7 @@ static __init int nx_compress_powernv_in + nx842_powernv_exec = nx842_exec_vas; + } + +- ret = crypto_register_alg(&nx842_powernv_alg); ++ ret = crypto_register_scomp(&nx842_powernv_alg); + if (ret) { + nx_delete_coprocs(); + return ret; +@@ -1131,7 +1130,7 @@ static void __exit nx_compress_powernv_e + if (!nx842_ct) + vas_unregister_api_powernv(); + +- crypto_unregister_alg(&nx842_powernv_alg); ++ crypto_unregister_scomp(&nx842_powernv_alg); + + nx_delete_coprocs(); + } +--- a/drivers/crypto/nx/nx-common-pseries.c ++++ b/drivers/crypto/nx/nx-common-pseries.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + + #include "nx-842.h" + #include "nx_csbcpb.h" /* struct nx_csbcpb */ +@@ -1006,23 +1007,21 @@ static struct nx842_driver nx842_pseries + .decompress = nx842_pseries_decompress, + }; + +-static int nx842_pseries_crypto_init(struct crypto_tfm *tfm) ++static void *nx842_pseries_crypto_alloc_ctx(void) + { +- return nx842_crypto_init(tfm, &nx842_pseries_driver); ++ return nx842_crypto_alloc_ctx(&nx842_pseries_driver); + } + +-static struct crypto_alg nx842_pseries_alg = { +- .cra_name = "842", +- .cra_driver_name = "842-nx", +- .cra_priority = 300, +- .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, +- .cra_ctxsize = sizeof(struct nx842_crypto_ctx), +- .cra_module = THIS_MODULE, +- .cra_init = nx842_pseries_crypto_init, +- .cra_exit = nx842_crypto_exit, +- .cra_u = { .compress = { +- .coa_compress = nx842_crypto_compress, +- .coa_decompress = nx842_crypto_decompress } } ++static struct scomp_alg nx842_pseries_alg = { ++ .base.cra_name = "842", ++ .base.cra_driver_name = "842-nx", ++ .base.cra_priority = 300, ++ .base.cra_module = THIS_MODULE, ++ ++ .alloc_ctx = nx842_pseries_crypto_alloc_ctx, ++ .free_ctx = nx842_crypto_free_ctx, ++ .compress = nx842_crypto_compress, ++ .decompress = nx842_crypto_decompress, + }; + + static int nx842_probe(struct vio_dev *viodev, +@@ -1070,7 +1069,7 @@ static int nx842_probe(struct vio_dev *v + if (ret) + goto error; + +- ret = crypto_register_alg(&nx842_pseries_alg); ++ ret = crypto_register_scomp(&nx842_pseries_alg); + if (ret) { + dev_err(&viodev->dev, "could not register comp alg: %d\n", ret); + goto error; +@@ -1118,7 +1117,7 @@ static void nx842_remove(struct vio_dev + if (caps_feat) + sysfs_remove_group(&viodev->dev.kobj, &nxcop_caps_attr_group); + +- crypto_unregister_alg(&nx842_pseries_alg); ++ crypto_unregister_scomp(&nx842_pseries_alg); + + spin_lock_irqsave(&devdata_mutex, flags); + old_devdata = rcu_dereference_check(devdata, +@@ -1247,7 +1246,7 @@ static void __exit nx842_pseries_exit(vo + + vas_unregister_api_pseries(); + +- crypto_unregister_alg(&nx842_pseries_alg); ++ crypto_unregister_scomp(&nx842_pseries_alg); + + spin_lock_irqsave(&devdata_mutex, flags); + old_devdata = rcu_dereference_check(devdata, diff --git a/queue-5.15/erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch b/queue-5.15/erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch new file mode 100644 index 0000000000..c470e4b1b3 --- /dev/null +++ b/queue-5.15/erofs-fix-the-out-of-bounds-nameoff-handling-for-trailing-dirents.patch @@ -0,0 +1,95 @@ +From stable+bounces-242493-greg=kroah.com@vger.kernel.org Sat May 2 00:18:56 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 14:48:48 -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: <20260501184848.3924034-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 +@@ -37,20 +37,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, +@@ -62,6 +60,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) +@@ -95,8 +97,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.15/erofs-fix-unsigned-underflow-in-z_erofs_lz4_handle_overlap.patch b/queue-5.15/erofs-fix-unsigned-underflow-in-z_erofs_lz4_handle_overlap.patch new file mode 100644 index 0000000000..a4c8c62974 --- /dev/null +++ b/queue-5.15/erofs-fix-unsigned-underflow-in-z_erofs_lz4_handle_overlap.patch @@ -0,0 +1,58 @@ +From stable+bounces-244954-greg=kroah.com@vger.kernel.org Sat May 9 18:22:53 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 08:49:57 -0400 +Subject: erofs: fix unsigned underflow in z_erofs_lz4_handle_overlap() +To: stable@vger.kernel.org +Cc: Junrui Luo , Yuhao Jiang , Gao Xiang , Sasha Levin +Message-ID: <20260509124957.3382270-1-sashal@kernel.org> + +From: Junrui Luo + +[ Upstream commit 21e161de2dc660b1bb70ef5b156ab8e6e1cca3ab ] + +Some crafted images can have illegal (!partial_decoding && +m_llen < m_plen) extents, and the LZ4 inplace decompression path +can be wrongly hit, but it cannot handle (outpages < inpages) +properly: "outpages - inpages" wraps to a large value and +the subsequent rq->out[] access reads past the decompressed_pages +array. + +However, such crafted cases can correctly result in a corruption +report in the normal LZ4 non-inplace path. + +Let's add an additional check to fix this for backporting. + +Reproducible image (base64-encoded gzipped blob): + +H4sIAJGR12kCA+3SPUoDQRgG4MkmkkZk8QRbRFIIi9hbpEjrHQI5ghfwCN5BLCzTGtLbBI+g +dilSJo1CnIm7GEXFxhT6PDDwfrs73/ywIQD/1ePD4r7Ou6ETsrq4mu7XcWfj++Pb58nJU/9i +PNtbjhan04/9GtX4qVYc814WDqt6FaX5s+ZwXXeq52lndT6IuVvlblytLMvh4Gzwaf90nsvz +2DF/21+20T/ldgp5s1jXRaN4t/8izsy/OUB6e/Qa79r+JwAAAAAAAL52vQVuGQAAAP6+my1w +ywAAAAAAAADwu14ATsEYtgBQAAA= + +$ mount -t erofs -o cache_strategy=disabled foo.erofs /mnt +$ dd if=/mnt/data of=/dev/null bs=4096 count=1 + +Fixes: 598162d05080 ("erofs: support decompress big pcluster for lz4 backend") +Reported-by: Yuhao Jiang +Cc: stable@vger.kernel.org +Signed-off-by: Junrui Luo +Reviewed-by: Gao Xiang +Signed-off-by: Gao Xiang +[ renamed `rq->outpages`/`rq->inpages` to `nrpages_out`/`nrpages_in` and inverted the check to `nrpages_out < nrpages_in` on the existing `goto docopy` chain ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/erofs/decompressor.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/erofs/decompressor.c ++++ b/fs/erofs/decompressor.c +@@ -140,6 +140,7 @@ static void *z_erofs_handle_inplace_io(s + + if (rq->inplace_io) { + if (rq->partial_decoding || !support_0padding || ++ nrpages_out < nrpages_in || + ofull - oend < LZ4_DECOMPRESS_INPLACE_MARGIN(inputsize)) + goto docopy; + diff --git a/queue-5.15/f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch b/queue-5.15/f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch new file mode 100644 index 0000000000..636d9ca428 --- /dev/null +++ b/queue-5.15/f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch @@ -0,0 +1,150 @@ +From stable+bounces-240661-greg=kroah.com@vger.kernel.org Fri Apr 24 18:58:40 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 09:28:31 -0400 +Subject: f2fs: fix to do sanity check on dcc->discard_cmd_cnt conditionally +To: stable@vger.kernel.org +Cc: Chao Yu , stable@kernel.org, syzbot+62538b67389ee582837a@syzkaller.appspotmail.com, Jaegeuk Kim , Sasha Levin +Message-ID: <20260424132831.1946653-1-sashal@kernel.org> + +From: Chao Yu + +[ Upstream commit 6af249c996f7d73a3435f9e577956fa259347d18 ] + +Syzbot reported a f2fs bug as below: + +------------[ cut here ]------------ +kernel BUG at fs/f2fs/segment.c:1900! +Oops: invalid opcode: 0000 [#1] SMP KASAN PTI +CPU: 1 UID: 0 PID: 6527 Comm: syz.5.110 Not tainted syzkaller #0 PREEMPT_{RT,(full)} +Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 02/12/2026 +RIP: 0010:f2fs_issue_discard_timeout+0x59b/0x5a0 fs/f2fs/segment.c:1900 +Code: d9 80 e1 07 80 c1 03 38 c1 0f 8c d6 fe ff ff 48 89 df e8 a8 5e fa fd e9 c9 fe ff ff e8 4e 46 94 fd 90 0f 0b e8 46 46 94 fd 90 <0f> 0b 0f 1f 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 +RSP: 0018:ffffc9000494f940 EFLAGS: 00010283 +RAX: ffffffff843009ca RBX: 0000000000000001 RCX: 0000000000080000 +RDX: ffffc9001ca78000 RSI: 00000000000029f3 RDI: 00000000000029f4 +RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000 +R10: dffffc0000000000 R11: ffffed100893a431 R12: 1ffff1100893a430 +R13: 1ffff1100c2b702c R14: dffffc0000000000 R15: ffff8880449d2160 +FS: 00007ffa35fed6c0(0000) GS:ffff88812643d000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 00007f2b68634000 CR3: 0000000039f62000 CR4: 00000000003526f0 +Call Trace: + + __f2fs_remount fs/f2fs/super.c:2960 [inline] + f2fs_reconfigure+0x108a/0x1710 fs/f2fs/super.c:5443 + reconfigure_super+0x227/0x8a0 fs/super.c:1080 + do_remount fs/namespace.c:3391 [inline] + path_mount+0xdc5/0x10e0 fs/namespace.c:4151 + do_mount fs/namespace.c:4172 [inline] + __do_sys_mount fs/namespace.c:4361 [inline] + __se_sys_mount+0x31d/0x420 fs/namespace.c:4338 + do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] + do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94 + entry_SYSCALL_64_after_hwframe+0x77/0x7f +RIP: 0033:0x7ffa37dbda0a + +The root cause is there will be race condition in between f2fs_ioc_fitrim() +and f2fs_remount(): + +- f2fs_remount - f2fs_ioc_fitrim + - f2fs_issue_discard_timeout + - __issue_discard_cmd + - __drop_discard_cmd + - __wait_all_discard_cmd + - f2fs_trim_fs + - f2fs_write_checkpoint + - f2fs_clear_prefree_segments + - f2fs_issue_discard + - __issue_discard_async + - __queue_discard_cmd + - __update_discard_tree_range + - __insert_discard_cmd + - __create_discard_cmd + : atomic_inc(&dcc->discard_cmd_cnt); + - sanity check on dcc->discard_cmd_cnt (expect discard_cmd_cnt to be zero) + +This will only happen when fitrim races w/ remount rw, if we remount to +readonly filesystem, remount will wait until mnt_pcp.mnt_writers to zero, +that means fitrim is not in process at that time. + +Cc: stable@kernel.org +Fixes: 2482c4325dfe ("f2fs: detect bug_on in f2fs_wait_discard_bios") +Reported-by: syzbot+62538b67389ee582837a@syzkaller.appspotmail.com +Closes: https://lore.kernel.org/linux-f2fs-devel/69b07d7c.050a0220.8df7.09a1.GAE@google.com +Signed-off-by: Chao Yu +Signed-off-by: Jaegeuk Kim +[ Different function signatures ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/f2fs/f2fs.h | 2 +- + fs/f2fs/segment.c | 6 +++--- + fs/f2fs/super.c | 9 +++++++-- + 3 files changed, 11 insertions(+), 6 deletions(-) + +--- a/fs/f2fs/f2fs.h ++++ b/fs/f2fs/f2fs.h +@@ -3467,7 +3467,7 @@ bool f2fs_is_checkpointed_data(struct f2 + int f2fs_start_discard_thread(struct f2fs_sb_info *sbi); + void f2fs_drop_discard_cmd(struct f2fs_sb_info *sbi); + void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi); +-bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi); ++bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi, bool need_check); + void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi, + struct cp_control *cpc); + void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi); +--- a/fs/f2fs/segment.c ++++ b/fs/f2fs/segment.c +@@ -1741,7 +1741,7 @@ void f2fs_stop_discard_thread(struct f2f + } + + /* This comes from f2fs_put_super */ +-bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi) ++bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi, bool need_check) + { + struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + struct discard_policy dpolicy; +@@ -1755,7 +1755,7 @@ bool f2fs_issue_discard_timeout(struct f + /* just to make sure there is no pending discard commands */ + __wait_all_discard_cmd(sbi, NULL); + +- f2fs_bug_on(sbi, atomic_read(&dcc->discard_cmd_cnt)); ++ f2fs_bug_on(sbi, need_check && atomic_read(&dcc->discard_cmd_cnt)); + return dropped; + } + +@@ -2197,7 +2197,7 @@ static void destroy_discard_cmd_control( + * fill_super(), it needs to give a chance to handle them. + */ + if (unlikely(atomic_read(&dcc->discard_cmd_cnt))) +- f2fs_issue_discard_timeout(sbi); ++ f2fs_issue_discard_timeout(sbi, true); + + kfree(dcc); + SM_I(sbi)->dcc_info = NULL; +--- a/fs/f2fs/super.c ++++ b/fs/f2fs/super.c +@@ -1600,7 +1600,7 @@ static void f2fs_put_super(struct super_ + } + + /* be sure to wait for any on-going discard commands */ +- dropped = f2fs_issue_discard_timeout(sbi); ++ dropped = f2fs_issue_discard_timeout(sbi, true); + + if ((f2fs_hw_support_discard(sbi) || f2fs_hw_should_discard(sbi)) && + !sbi->discard_blks && !dropped) { +@@ -2409,8 +2409,13 @@ static int f2fs_remount(struct super_blo + } else { + dcc = SM_I(sbi)->dcc_info; + f2fs_stop_discard_thread(sbi); ++ /* ++ * f2fs_ioc_fitrim() won't race w/ "remount ro" ++ * so it's safe to check discard_cmd_cnt in ++ * f2fs_issue_discard_timeout(). ++ */ + if (atomic_read(&dcc->discard_cmd_cnt)) +- f2fs_issue_discard_timeout(sbi); ++ f2fs_issue_discard_timeout(sbi, *flags & SB_RDONLY); + need_restart_discard = true; + } + } diff --git a/queue-5.15/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch b/queue-5.15/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch new file mode 100644 index 0000000000..89b7bc00d2 --- /dev/null +++ b/queue-5.15/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch @@ -0,0 +1,76 @@ +From stable+bounces-240930-greg=kroah.com@vger.kernel.org Fri Apr 24 19:15:18 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 09:43:22 -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: <20260424134323.2017162-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 +@@ -339,6 +339,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); + +@@ -350,8 +352,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_page_private_gcing(page); + end_page_writeback(page); + } diff --git a/queue-5.15/fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch b/queue-5.15/fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch new file mode 100644 index 0000000000..a880e7675a --- /dev/null +++ b/queue-5.15/fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch @@ -0,0 +1,333 @@ +From stable+bounces-244037-greg=kroah.com@vger.kernel.org Tue May 5 15:19:47 2026 +From: Sasha Levin +Date: Tue, 5 May 2026 05:49:37 -0400 +Subject: fbdev: defio: Disconnect deferred I/O from the lifetime of struct fb_info +To: stable@vger.kernel.org +Cc: Thomas Zimmermann , Helge Deller , linux-fbdev@vger.kernel.org, dri-devel@lists.freedesktop.org, Sasha Levin +Message-ID: <20260505094937.506038-1-sashal@kernel.org> + +From: Thomas Zimmermann + +[ Upstream commit 9ded47ad003f09a94b6a710b5c47f4aa5ceb7429 ] + +Hold state of deferred I/O in struct fb_deferred_io_state. Allocate an +instance as part of initializing deferred I/O and remove it only after +the final mapping has been closed. If the fb_info and the contained +deferred I/O meanwhile goes away, clear struct fb_deferred_io_state.info +to invalidate the mapping. Any access will then result in a SIGBUS +signal. + +Fixes a long-standing problem, where a device hot-unplug happens while +user space still has an active mapping of the graphics memory. The hot- +unplug frees the instance of struct fb_info. Accessing the memory will +operate on undefined state. + +Signed-off-by: Thomas Zimmermann +Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support") +Cc: Helge Deller +Cc: linux-fbdev@vger.kernel.org +Cc: dri-devel@lists.freedesktop.org +Cc: stable@vger.kernel.org # v2.6.22+ +Signed-off-by: Helge Deller +[ replaced `kzalloc_obj()` with `kzalloc(sizeof(*fbdefio_state), GFP_KERNEL)` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/video/fbdev/core/fb_defio.c | 152 +++++++++++++++++++++++++++++++----- + include/linux/fb.h | 4 + 2 files changed, 138 insertions(+), 18 deletions(-) + +--- a/drivers/video/fbdev/core/fb_defio.c ++++ b/drivers/video/fbdev/core/fb_defio.c +@@ -23,6 +23,75 @@ + #include + #include + ++/* ++ * struct fb_deferred_io_state ++ */ ++ ++struct fb_deferred_io_state { ++ struct kref ref; ++ ++ struct mutex lock; /* mutex that protects the pageref list */ ++ /* fields protected by lock */ ++ struct fb_info *info; ++}; ++ ++static struct fb_deferred_io_state *fb_deferred_io_state_alloc(void) ++{ ++ struct fb_deferred_io_state *fbdefio_state; ++ ++ fbdefio_state = kzalloc(sizeof(*fbdefio_state), GFP_KERNEL); ++ if (!fbdefio_state) ++ return NULL; ++ ++ kref_init(&fbdefio_state->ref); ++ mutex_init(&fbdefio_state->lock); ++ ++ return fbdefio_state; ++} ++ ++static void fb_deferred_io_state_release(struct fb_deferred_io_state *fbdefio_state) ++{ ++ mutex_destroy(&fbdefio_state->lock); ++ ++ kfree(fbdefio_state); ++} ++ ++static void fb_deferred_io_state_get(struct fb_deferred_io_state *fbdefio_state) ++{ ++ kref_get(&fbdefio_state->ref); ++} ++ ++static void __fb_deferred_io_state_release(struct kref *ref) ++{ ++ struct fb_deferred_io_state *fbdefio_state = ++ container_of(ref, struct fb_deferred_io_state, ref); ++ ++ fb_deferred_io_state_release(fbdefio_state); ++} ++ ++static void fb_deferred_io_state_put(struct fb_deferred_io_state *fbdefio_state) ++{ ++ kref_put(&fbdefio_state->ref, __fb_deferred_io_state_release); ++} ++ ++/* ++ * struct vm_operations_struct ++ */ ++ ++static void fb_deferred_io_vm_open(struct vm_area_struct *vma) ++{ ++ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data; ++ ++ fb_deferred_io_state_get(fbdefio_state); ++} ++ ++static void fb_deferred_io_vm_close(struct vm_area_struct *vma) ++{ ++ struct fb_deferred_io_state *fbdefio_state = vma->vm_private_data; ++ ++ fb_deferred_io_state_put(fbdefio_state); ++} ++ + static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) + { + void *screen_base = (void __force *) info->screen_base; +@@ -93,17 +162,31 @@ static void fb_deferred_io_pageref_put(s + /* this is to find and return the vmalloc-ed fb pages */ + static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf) + { ++ struct fb_info *info; + unsigned long offset; + struct page *page; +- struct fb_info *info = vmf->vma->vm_private_data; ++ vm_fault_t ret; ++ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data; ++ ++ mutex_lock(&fbdefio_state->lock); ++ ++ info = fbdefio_state->info; ++ if (!info) { ++ ret = VM_FAULT_SIGBUS; /* our device is gone */ ++ goto err_mutex_unlock; ++ } + + offset = vmf->pgoff << PAGE_SHIFT; +- if (offset >= info->fix.smem_len) +- return VM_FAULT_SIGBUS; ++ if (offset >= info->fix.smem_len) { ++ ret = VM_FAULT_SIGBUS; ++ goto err_mutex_unlock; ++ } + + page = fb_deferred_io_page(info, offset); +- if (!page) +- return VM_FAULT_SIGBUS; ++ if (!page) { ++ ret = VM_FAULT_SIGBUS; ++ goto err_mutex_unlock; ++ } + + get_page(page); + +@@ -115,8 +198,14 @@ static vm_fault_t fb_deferred_io_fault(s + BUG_ON(!page->mapping); + page->index = vmf->pgoff; /* for page_mkclean() */ + ++ mutex_unlock(&fbdefio_state->lock); ++ + vmf->page = page; + return 0; ++ ++err_mutex_unlock: ++ mutex_unlock(&fbdefio_state->lock); ++ return ret; + } + + int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasync) +@@ -143,8 +232,9 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); + static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf) + { + struct page *page = vmf->page; +- struct fb_info *info = vmf->vma->vm_private_data; +- struct fb_deferred_io *fbdefio = info->fbdefio; ++ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data; ++ struct fb_info *info; ++ struct fb_deferred_io *fbdefio; + struct fb_deferred_io_pageref *pageref; + unsigned long offset; + vm_fault_t ret; +@@ -160,7 +250,15 @@ static vm_fault_t fb_deferred_io_mkwrite + file_update_time(vmf->vma->vm_file); + + /* protect against the workqueue changing the page list */ +- mutex_lock(&fbdefio->lock); ++ mutex_lock(&fbdefio_state->lock); ++ ++ info = fbdefio_state->info; ++ if (!info) { ++ ret = VM_FAULT_SIGBUS; /* our device is gone */ ++ goto err_mutex_unlock; ++ } ++ ++ fbdefio = info->fbdefio; + + /* first write in this cycle, notify the driver */ + if (fbdefio->first_io && list_empty(&fbdefio->pagereflist)) +@@ -182,18 +280,20 @@ static vm_fault_t fb_deferred_io_mkwrite + */ + lock_page(pageref->page); + +- mutex_unlock(&fbdefio->lock); ++ mutex_unlock(&fbdefio_state->lock); + + /* come back after delay to process the deferred IO */ + schedule_delayed_work(&info->deferred_work, fbdefio->delay); + return VM_FAULT_LOCKED; + + err_mutex_unlock: +- mutex_unlock(&fbdefio->lock); ++ mutex_unlock(&fbdefio_state->lock); + return ret; + } + + static const struct vm_operations_struct fb_deferred_io_vm_ops = { ++ .open = fb_deferred_io_vm_open, ++ .close = fb_deferred_io_vm_close, + .fault = fb_deferred_io_fault, + .page_mkwrite = fb_deferred_io_mkwrite, + }; +@@ -215,7 +315,10 @@ int fb_deferred_io_mmap(struct fb_info * + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + if (!(info->flags & FBINFO_VIRTFB)) + vma->vm_flags |= VM_IO; +- vma->vm_private_data = info; ++ vma->vm_private_data = info->fbdefio_state; ++ ++ fb_deferred_io_state_get(info->fbdefio_state); /* released in vma->vm_ops->close() */ ++ + return 0; + } + +@@ -225,9 +328,10 @@ static void fb_deferred_io_work(struct w + struct fb_info *info = container_of(work, struct fb_info, deferred_work.work); + struct fb_deferred_io_pageref *pageref, *next; + struct fb_deferred_io *fbdefio = info->fbdefio; ++ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; + + /* here we mkclean the pages, then do all deferred IO */ +- mutex_lock(&fbdefio->lock); ++ mutex_lock(&fbdefio_state->lock); + list_for_each_entry(pageref, &fbdefio->pagereflist, list) { + struct page *cur = pageref->page; + lock_page(cur); +@@ -242,12 +346,13 @@ static void fb_deferred_io_work(struct w + list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list) + fb_deferred_io_pageref_put(pageref, info); + +- mutex_unlock(&fbdefio->lock); ++ mutex_unlock(&fbdefio_state->lock); + } + + int fb_deferred_io_init(struct fb_info *info) + { + struct fb_deferred_io *fbdefio = info->fbdefio; ++ struct fb_deferred_io_state *fbdefio_state; + struct fb_deferred_io_pageref *pagerefs; + unsigned long npagerefs, i; + int ret; +@@ -257,7 +362,11 @@ int fb_deferred_io_init(struct fb_info * + if (WARN_ON(!info->fix.smem_len)) + return -EINVAL; + +- mutex_init(&fbdefio->lock); ++ fbdefio_state = fb_deferred_io_state_alloc(); ++ if (!fbdefio_state) ++ return -ENOMEM; ++ fbdefio_state->info = info; ++ + INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work); + INIT_LIST_HEAD(&fbdefio->pagereflist); + if (fbdefio->delay == 0) /* set a default of 1 s */ +@@ -276,10 +385,12 @@ int fb_deferred_io_init(struct fb_info * + info->npagerefs = npagerefs; + info->pagerefs = pagerefs; + ++ info->fbdefio_state = fbdefio_state; ++ + return 0; + + err: +- mutex_destroy(&fbdefio->lock); ++ fb_deferred_io_state_release(fbdefio_state); + return ret; + } + EXPORT_SYMBOL_GPL(fb_deferred_io_init); +@@ -320,11 +431,18 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_release + + void fb_deferred_io_cleanup(struct fb_info *info) + { +- struct fb_deferred_io *fbdefio = info->fbdefio; ++ struct fb_deferred_io_state *fbdefio_state = info->fbdefio_state; + + fb_deferred_io_lastclose(info); + ++ info->fbdefio_state = NULL; ++ ++ mutex_lock(&fbdefio_state->lock); ++ fbdefio_state->info = NULL; ++ mutex_unlock(&fbdefio_state->lock); ++ ++ fb_deferred_io_state_put(fbdefio_state); ++ + kvfree(info->pagerefs); +- mutex_destroy(&fbdefio->lock); + } + EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup); +--- a/include/linux/fb.h ++++ b/include/linux/fb.h +@@ -213,12 +213,13 @@ struct fb_deferred_io { + unsigned long delay; + bool sort_pagereflist; /* sort pagelist by offset */ + int open_count; /* number of opened files; protected by fb_info lock */ +- struct mutex lock; /* mutex that protects the pageref list */ + struct list_head pagereflist; /* list of pagerefs for touched pages */ + /* callback */ + void (*first_io)(struct fb_info *info); + void (*deferred_io)(struct fb_info *info, struct list_head *pagelist); + }; ++ ++struct fb_deferred_io_state; + #endif + + /* +@@ -480,6 +481,7 @@ struct fb_info { + unsigned long npagerefs; + struct fb_deferred_io_pageref *pagerefs; + struct fb_deferred_io *fbdefio; ++ struct fb_deferred_io_state *fbdefio_state; + #endif + + const struct fb_ops *fbops; diff --git a/queue-5.15/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch b/queue-5.15/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch new file mode 100644 index 0000000000..0750a84288 --- /dev/null +++ b/queue-5.15/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch @@ -0,0 +1,142 @@ +From stable+bounces-244872-greg=kroah.com@vger.kernel.org Sat May 9 06:43:11 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 21:12:59 -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: <20260509011259.2724832-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.15/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch b/queue-5.15/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch new file mode 100644 index 0000000000..9accdcdd28 --- /dev/null +++ b/queue-5.15/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch @@ -0,0 +1,189 @@ +From stable+bounces-244871-greg=kroah.com@vger.kernel.org Sat May 9 06:43:07 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 21:12:58 -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: <20260509011259.2724832-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 +@@ -533,6 +533,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.15/io_uring-poll-fix-signed-comparison-in-io_poll_get_ownership.patch b/queue-5.15/io_uring-poll-fix-signed-comparison-in-io_poll_get_ownership.patch new file mode 100644 index 0000000000..7c4e8848f9 --- /dev/null +++ b/queue-5.15/io_uring-poll-fix-signed-comparison-in-io_poll_get_ownership.patch @@ -0,0 +1,52 @@ +From 4f1ddce4042ea4b3d8a3d14d1b4745d408b68090 Mon Sep 17 00:00:00 2001 +From: Longxuan Yu +Date: Sun, 12 Apr 2026 16:38:20 +0800 +Subject: io_uring/poll: fix signed comparison in io_poll_get_ownership() + +From: Longxuan Yu + +Commit 326941b22806cbf2df1fbfe902b7908b368cce42 usptream. + +io_poll_get_ownership() uses a signed comparison to check whether +poll_refs has reached the threshold for the slowpath: + + if (unlikely(atomic_read(&req->poll_refs) >= IO_POLL_REF_BIAS)) + +atomic_read() returns int (signed). When IO_POLL_CANCEL_FLAG +(BIT(31)) is set in poll_refs, the value becomes negative in +signed arithmetic, so the >= 128 comparison always evaluates to +false and the slowpath is never taken. + +Fix this by casting the atomic_read() result to unsigned int +before the comparison, so that the cancel flag is treated as a +large positive value and correctly triggers the slowpath. + +Fixes: a26a35e9019f ("io_uring: make poll refs more robust") +Cc: stable@vger.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: Zhengchuan Liang +Signed-off-by: Longxuan Yu +Signed-off-by: Ren Wei +Reviewed-by: Pavel Begunkov +Link: https://patch.msgid.link/3a3508b08bcd7f1bc3beff848ae6e1d73d355043.1775965597.git.ylong030@ucr.edu +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman +--- + io_uring/io_uring.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/io_uring/io_uring.c ++++ b/io_uring/io_uring.c +@@ -5525,7 +5525,7 @@ static bool io_poll_get_ownership_slowpa + */ + static inline bool io_poll_get_ownership(struct io_kiocb *req) + { +- if (unlikely(atomic_read(&req->poll_refs) >= IO_POLL_REF_BIAS)) ++ if (unlikely((unsigned int)atomic_read(&req->poll_refs) >= IO_POLL_REF_BIAS)) + return io_poll_get_ownership_slowpath(req); + return !(atomic_fetch_inc(&req->poll_refs) & IO_POLL_REF_MASK); + } diff --git a/queue-5.15/ksmbd-compare-macs-in-constant-time.patch b/queue-5.15/ksmbd-compare-macs-in-constant-time.patch new file mode 100644 index 0000000000..16501b974d --- /dev/null +++ b/queue-5.15/ksmbd-compare-macs-in-constant-time.patch @@ -0,0 +1,83 @@ +From c5794709bc9105935dbedef8b9cf9c06f2b559fa Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Tue, 17 Feb 2026 20:28:29 -0800 +Subject: ksmbd: Compare MACs in constant time + +From: Eric Biggers + +commit c5794709bc9105935dbedef8b9cf9c06f2b559fa upstream. + +To prevent timing attacks, MAC comparisons need to be constant-time. +Replace the memcmp() with the correct function, crypto_memneq(). + +Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") +Cc: stable@vger.kernel.org +Signed-off-by: Eric Biggers +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Rajani Kantha <681739313@139.com> +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/Kconfig | 1 + + fs/ksmbd/auth.c | 4 +++- + fs/ksmbd/smb2pdu.c | 5 +++-- + 3 files changed, 7 insertions(+), 3 deletions(-) + +--- a/fs/ksmbd/Kconfig ++++ b/fs/ksmbd/Kconfig +@@ -10,6 +10,7 @@ config SMB_SERVER + select CRYPTO_HMAC + select CRYPTO_ECB + select CRYPTO_LIB_DES ++ select CRYPTO_LIB_UTILS + select CRYPTO_SHA256 + select CRYPTO_CMAC + select CRYPTO_SHA512 +--- a/fs/ksmbd/auth.c ++++ b/fs/ksmbd/auth.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -281,7 +282,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn + goto out; + } + +- if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0) ++ if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp, ++ CIFS_HMAC_MD5_HASH_SIZE)) + rc = -EINVAL; + out: + if (ctx) +--- a/fs/ksmbd/smb2pdu.c ++++ b/fs/ksmbd/smb2pdu.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2018 Samsung Electronics Co., Ltd. + */ + ++#include + #include + #include + #include +@@ -8440,7 +8441,7 @@ int smb2_check_sign_req(struct ksmbd_wor + signature)) + return 0; + +- if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { ++ if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } +@@ -8528,7 +8529,7 @@ int smb3_check_sign_req(struct ksmbd_wor + if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature)) + return 0; + +- if (memcmp(signature, signature_req, SMB2_SIGNATURE_SIZE)) { ++ if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) { + pr_err("bad smb2 signature\n"); + return 0; + } diff --git a/queue-5.15/ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch b/queue-5.15/ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch new file mode 100644 index 0000000000..d78dd6ea26 --- /dev/null +++ b/queue-5.15/ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch @@ -0,0 +1,107 @@ +From stable+bounces-241014-greg=kroah.com@vger.kernel.org Fri Apr 24 22:57:03 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 13:25:12 -0400 +Subject: ksmbd: require minimum ACE size in smb_check_perm_dacl() +To: stable@vger.kernel.org +Cc: Michael Bommarito , Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260424172512.2372287-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit d07b26f39246a82399661936dd0c853983cfade7 ] + +Both ACE-walk loops in smb_check_perm_dacl() only guard against an +under-sized remaining buffer, not against an ACE whose declared +`ace->size` is smaller than the struct it claims to describe: + + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); + if (ace_size > aces_size) + break; + +The first check only requires the 4-byte ACE header to be in bounds; +it does not require access_req (4 bytes at offset 4) to be readable. +An attacker who has set a crafted DACL on a file they own can declare +ace->size == 4 with aces_size == 4, pass both checks, and then + + granted |= le32_to_cpu(ace->access_req); /* upper loop */ + compare_sids(&sid, &ace->sid); /* lower loop */ + +reads access_req at offset 4 (OOB by up to 4 bytes) and ace->sid at +offset 8 (OOB by up to CIFS_SID_BASE_SIZE + SID_MAX_SUB_AUTHORITIES +* 4 bytes). + +Tighten both loops to require + + ace_size >= offsetof(struct smb_ace, sid) + CIFS_SID_BASE_SIZE + +which is the smallest valid on-wire ACE layout (4-byte header + +4-byte access_req + 8-byte sid base with zero sub-auths). Also +reject ACEs whose sid.num_subauth exceeds SID_MAX_SUB_AUTHORITIES +before letting compare_sids() dereference sub_auth[] entries. + +parse_sec_desc() already enforces an equivalent check (lines 441-448); +smb_check_perm_dacl() simply grew weaker validation over time. + +Reachability: authenticated SMB client with permission to set an ACL +on a file. On a subsequent CREATE against that file, the kernel +walks the stored DACL via smb_check_perm_dacl() and triggers the +OOB read. Not pre-auth, and the OOB read is not reflected to the +attacker, but KASAN reports and kernel state corruption are +possible. + +Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-6 +Assisted-by: Codex:gpt-5-4 +Signed-off-by: Michael Bommarito +Acked-by: Namjae Jeon +Signed-off-by: Steve French +[ changed le16_to_cpu to le32_to_cpu for num_aces field which is __le32 ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/smbacl.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +--- a/fs/ksmbd/smbacl.c ++++ b/fs/ksmbd/smbacl.c +@@ -1265,10 +1265,13 @@ int smb_check_perm_dacl(struct ksmbd_con + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { +- if (offsetof(struct smb_ace, access_req) > aces_size) ++ if (offsetof(struct smb_ace, sid) + ++ aces_size < CIFS_SID_BASE_SIZE) + break; + ace_size = le16_to_cpu(ace->size); +- if (ace_size > aces_size) ++ if (ace_size > aces_size || ++ ace_size < offsetof(struct smb_ace, sid) + ++ CIFS_SID_BASE_SIZE) + break; + aces_size -= ace_size; + granted |= le32_to_cpu(ace->access_req); +@@ -1286,13 +1289,19 @@ int smb_check_perm_dacl(struct ksmbd_con + ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + aces_size = acl_size - sizeof(struct smb_acl); + for (i = 0; i < le32_to_cpu(pdacl->num_aces); i++) { +- if (offsetof(struct smb_ace, access_req) > aces_size) ++ if (offsetof(struct smb_ace, sid) + ++ aces_size < CIFS_SID_BASE_SIZE) + break; + ace_size = le16_to_cpu(ace->size); +- if (ace_size > aces_size) ++ if (ace_size > aces_size || ++ ace_size < offsetof(struct smb_ace, sid) + ++ CIFS_SID_BASE_SIZE) + break; + aces_size -= ace_size; + ++ if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) ++ break; ++ + if (!compare_sids(&sid, &ace->sid) || + !compare_sids(&sid_unix_NFS_mode, &ace->sid)) { + found = 1; diff --git a/queue-5.15/lib-crypto-mpi-fix-integer-underflow-in-mpi_read_raw_from_sgl.patch b/queue-5.15/lib-crypto-mpi-fix-integer-underflow-in-mpi_read_raw_from_sgl.patch new file mode 100644 index 0000000000..344170ba9f --- /dev/null +++ b/queue-5.15/lib-crypto-mpi-fix-integer-underflow-in-mpi_read_raw_from_sgl.patch @@ -0,0 +1,70 @@ +From 8c2f1288250a90a4b5cabed5d888d7e3aeed4035 Mon Sep 17 00:00:00 2001 +From: Lukas Wunner +Date: Sun, 12 Apr 2026 16:19:47 +0200 +Subject: lib/crypto: mpi: Fix integer underflow in mpi_read_raw_from_sgl() + +From: Lukas Wunner + +commit 8c2f1288250a90a4b5cabed5d888d7e3aeed4035 upstream. + +Yiming reports an integer underflow in mpi_read_raw_from_sgl() when +subtracting "lzeros" from the unsigned "nbytes". + +For this to happen, the scatterlist "sgl" needs to occupy more bytes +than the "nbytes" parameter and the first "nbytes + 1" bytes of the +scatterlist must be zero. Under these conditions, the while loop +iterating over the scatterlist will count more zeroes than "nbytes", +subtract the number of zeroes from "nbytes" and cause the underflow. + +When commit 2d4d1eea540b ("lib/mpi: Add mpi sgl helpers") originally +introduced the bug, it couldn't be triggered because all callers of +mpi_read_raw_from_sgl() passed a scatterlist whose length was equal to +"nbytes". + +However since commit 63ba4d67594a ("KEYS: asymmetric: Use new crypto +interface without scatterlists"), the underflow can now actually be +triggered. When invoking a KEYCTL_PKEY_ENCRYPT system call with a +larger "out_len" than "in_len" and filling the "in" buffer with zeroes, +crypto_akcipher_sync_prep() will create an all-zero scatterlist used for +both the "src" and "dst" member of struct akcipher_request and thereby +fulfil the conditions to trigger the bug: + + sys_keyctl() + keyctl_pkey_e_d_s() + asymmetric_key_eds_op() + software_key_eds_op() + crypto_akcipher_sync_encrypt() + crypto_akcipher_sync_prep() + crypto_akcipher_encrypt() + rsa_enc() + mpi_read_raw_from_sgl() + +To the user this will be visible as a DoS as the kernel spins forever, +causing soft lockup splats as a side effect. + +Fix it. + +Reported-by: Yiming Qian # off-list +Fixes: 2d4d1eea540b ("lib/mpi: Add mpi sgl helpers") +Signed-off-by: Lukas Wunner +Cc: stable@vger.kernel.org # v4.4+ +Reviewed-by: Ignat Korchagin +Reviewed-by: Jarkko Sakkinen +Link: https://lore.kernel.org/r/59eca92ff4f87e2081777f1423a0efaaadcfdb39.1776003111.git.lukas@wunner.de +Signed-off-by: Eric Biggers +Signed-off-by: Greg Kroah-Hartman +--- + lib/mpi/mpicoder.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/lib/mpi/mpicoder.c ++++ b/lib/mpi/mpicoder.c +@@ -453,7 +453,7 @@ MPI mpi_read_raw_from_sgl(struct scatter + lzeros = 0; + len = 0; + while (nbytes > 0) { +- while (len && !*buff) { ++ while (len && !*buff && lzeros < nbytes) { + lzeros++; + len--; + buff++; diff --git a/queue-5.15/media-rc-igorplugusb-heed-coherency-rules.patch b/queue-5.15/media-rc-igorplugusb-heed-coherency-rules.patch new file mode 100644 index 0000000000..6fe6d4d6ef --- /dev/null +++ b/queue-5.15/media-rc-igorplugusb-heed-coherency-rules.patch @@ -0,0 +1,93 @@ +From stable+bounces-242572-greg=kroah.com@vger.kernel.org Sat May 2 07:42:26 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 22:12:21 -0400 +Subject: media: rc: igorplugusb: heed coherency rules +To: stable@vger.kernel.org +Cc: Oliver Neukum , Sean Young , Hans Verkuil , Sasha Levin +Message-ID: <20260502021221.4166866-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.15/media-rc-ttusbir-respect-dma-coherency-rules.patch b/queue-5.15/media-rc-ttusbir-respect-dma-coherency-rules.patch new file mode 100644 index 0000000000..24c98526d5 --- /dev/null +++ b/queue-5.15/media-rc-ttusbir-respect-dma-coherency-rules.patch @@ -0,0 +1,83 @@ +From stable+bounces-242479-greg=kroah.com@vger.kernel.org Fri May 1 22:58:01 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 13:27:52 -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: <20260501172752.3765703-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.15/mmc-sdhci-of-dwcmshc-disable-clock-before-dll-configuration.patch b/queue-5.15/mmc-sdhci-of-dwcmshc-disable-clock-before-dll-configuration.patch new file mode 100644 index 0000000000..a813ff5fa5 --- /dev/null +++ b/queue-5.15/mmc-sdhci-of-dwcmshc-disable-clock-before-dll-configuration.patch @@ -0,0 +1,75 @@ +From stable+bounces-244082-greg=kroah.com@vger.kernel.org Tue May 5 16:03:31 2026 +From: Sasha Levin +Date: Tue, 5 May 2026 06:18:15 -0400 +Subject: mmc: sdhci-of-dwcmshc: Disable clock before DLL configuration +To: stable@vger.kernel.org +Cc: Shawn Lin , Adrian Hunter , Ulf Hansson , Sasha Levin +Message-ID: <20260505101815.586456-1-sashal@kernel.org> + +From: Shawn Lin + +[ Upstream commit 6546a49bbe656981d99a389195560999058c89c4 ] + +According to the ASIC design recommendations, the clock must be +disabled before operating the DLL to prevent glitches that could +affect the internal digital logic. In extreme cases, failing to +do so may cause the controller to malfunction completely. + +Adds a step to disable the clock before DLL configuration and +re-enables it at the end. + +Fixes: 08f3dff799d4 ("mmc: sdhci-of-dwcmshc: add rockchip platform support") +Cc: stable@vger.kernel.org +Signed-off-by: Shawn Lin +Acked-by: Adrian Hunter +Signed-off-by: Ulf Hansson +[ dropped HS200/HS400 block and BIT(4) line, converted the single `return` in `if (clock <= 400000)` to `goto enable_clk` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mmc/host/sdhci-of-dwcmshc.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +--- a/drivers/mmc/host/sdhci-of-dwcmshc.c ++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c +@@ -213,10 +213,13 @@ static void dwcmshc_rk3568_set_clock(str + extra &= ~BIT(0); + sdhci_writel(host, extra, reg); + ++ /* Disable clock while config DLL */ ++ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); ++ + if (clock <= 400000) { + /* Disable DLL to reset sample clock */ + sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); +- return; ++ goto enable_clk; + } + + /* Reset DLL */ +@@ -234,7 +237,7 @@ static void dwcmshc_rk3568_set_clock(str + 500 * USEC_PER_MSEC); + if (err) { + dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n"); +- return; ++ goto enable_clk; + } + + extra = 0x1 << 16 | /* tune clock stop en */ +@@ -255,6 +258,16 @@ static void dwcmshc_rk3568_set_clock(str + DLL_STRBIN_TAPNUM_DEFAULT | + DLL_STRBIN_TAPNUM_FROM_SW; + sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); ++ ++enable_clk: ++ /* ++ * The sdclk frequency select bits in SDHCI_CLOCK_CONTROL are not functional ++ * on Rockchip's SDHCI implementation. Instead, the clock frequency is fully ++ * controlled via external clk provider by calling clk_set_rate(). Consequently, ++ * passing 0 to sdhci_enable_clk() only re-enables the already-configured clock, ++ * which matches the hardware's actual behavior. ++ */ ++ sdhci_enable_clk(host, 0); + } + + static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) diff --git a/queue-5.15/mtd-docg3-convert-to-platform-remove-callback-returning-void.patch b/queue-5.15/mtd-docg3-convert-to-platform-remove-callback-returning-void.patch new file mode 100644 index 0000000000..d85e0b05d0 --- /dev/null +++ b/queue-5.15/mtd-docg3-convert-to-platform-remove-callback-returning-void.patch @@ -0,0 +1,69 @@ +From stable+bounces-242570-greg=kroah.com@vger.kernel.org Sat May 2 07:42:01 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 22:11:53 -0400 +Subject: mtd: docg3: Convert to platform remove callback returning void +To: stable@vger.kernel.org +Cc: "Uwe Kleine-König" , "Miquel Raynal" , "Tudor Ambarus" , "Sasha Levin" +Message-ID: <20260502021154.4166366-1-sashal@kernel.org> + +From: Uwe Kleine-König + +[ Upstream commit eb0cec77d534413a800ec20944a2b1e37cfecdcf ] + +The .remove() callback for a platform driver returns an int which makes +many driver authors wrongly assume it's possible to do error handling by +returning an error code. However the value returned is ignored (apart +from emitting a warning) and this typically results in resource leaks. + +To improve here there is a quest to make the remove callback return +void. In the first step of this quest all drivers are converted to +.remove_new(), which already returns void. Eventually after all drivers +are converted, .remove_new() will be renamed to .remove(). + +Trivially convert this driver from always returning zero in the remove +callback to the void returning variant. + +Signed-off-by: Uwe Kleine-König +Signed-off-by: Miquel Raynal +Acked-by: Tudor Ambarus +Link: https://lore.kernel.org/linux-mtd/20231008200143.196369-5-u.kleine-koenig@pengutronix.de +Stable-dep-of: ca19808bc6fa ("mtd: docg3: fix use-after-free in docg3_release()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mtd/devices/docg3.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c +index 27c08f22dec8c..25a7df6448028 100644 +--- a/drivers/mtd/devices/docg3.c ++++ b/drivers/mtd/devices/docg3.c +@@ -2038,7 +2038,7 @@ static int __init docg3_probe(struct platform_device *pdev) + * + * Returns 0 + */ +-static int docg3_release(struct platform_device *pdev) ++static void docg3_release(struct platform_device *pdev) + { + struct docg3_cascade *cascade = platform_get_drvdata(pdev); + struct docg3 *docg3 = cascade->floors[0]->priv; +@@ -2050,7 +2050,6 @@ static int docg3_release(struct platform_device *pdev) + doc_release_device(cascade->floors[floor]); + + bch_free(docg3->cascade->bch); +- return 0; + } + + #ifdef CONFIG_OF +@@ -2068,7 +2067,7 @@ static struct platform_driver g3_driver = { + }, + .suspend = docg3_suspend, + .resume = docg3_resume, +- .remove = docg3_release, ++ .remove_new = docg3_release, + }; + + module_platform_driver_probe(g3_driver, docg3_probe); +-- +2.53.0 + diff --git a/queue-5.15/mtd-docg3-fix-use-after-free-in-docg3_release.patch b/queue-5.15/mtd-docg3-fix-use-after-free-in-docg3_release.patch new file mode 100644 index 0000000000..1ecea34524 --- /dev/null +++ b/queue-5.15/mtd-docg3-fix-use-after-free-in-docg3_release.patch @@ -0,0 +1,57 @@ +From stable+bounces-242571-greg=kroah.com@vger.kernel.org Sat May 2 07:42:01 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 22:11:54 -0400 +Subject: mtd: docg3: fix use-after-free in docg3_release() +To: stable@vger.kernel.org +Cc: James Kim , Miquel Raynal , Sasha Levin +Message-ID: <20260502021154.4166366-2-sashal@kernel.org> + +From: James Kim + +[ Upstream commit ca19808bc6fac7e29420d8508df569b346b3e339 ] + +In docg3_release(), the docg3 pointer is obtained from +cascade->floors[0]->priv before the loop that calls +doc_release_device() on each floor. doc_release_device() frees the +docg3 struct via kfree(docg3) at line 1881. After the loop, +docg3->cascade->bch dereferences the already-freed pointer. + +Fix this by accessing cascade->bch directly, which is equivalent +since docg3->cascade points back to the same cascade struct, and +is already available as a local variable. This also removes the +now-unused docg3 local variable. + +Fixes: c8ae3f744ddc ("lib/bch: Rework a little bit the exported function names") +Cc: stable@vger.kernel.org +Signed-off-by: James Kim +Signed-off-by: Miquel Raynal +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mtd/devices/docg3.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c +index 25a7df6448028..7de576404b14f 100644 +--- a/drivers/mtd/devices/docg3.c ++++ b/drivers/mtd/devices/docg3.c +@@ -2041,7 +2041,6 @@ static int __init docg3_probe(struct platform_device *pdev) + static void docg3_release(struct platform_device *pdev) + { + struct docg3_cascade *cascade = platform_get_drvdata(pdev); +- struct docg3 *docg3 = cascade->floors[0]->priv; + int floor; + + doc_unregister_sysfs(pdev, cascade); +@@ -2049,7 +2048,7 @@ static void docg3_release(struct platform_device *pdev) + if (cascade->floors[floor]) + doc_release_device(cascade->floors[floor]); + +- bch_free(docg3->cascade->bch); ++ bch_free(cascade->bch); + } + + #ifdef CONFIG_OF +-- +2.53.0 + diff --git a/queue-5.15/mtd-spi-nor-sst-fix-write-enable-before-aai-sequence.patch b/queue-5.15/mtd-spi-nor-sst-fix-write-enable-before-aai-sequence.patch new file mode 100644 index 0000000000..e3b1e83d01 --- /dev/null +++ b/queue-5.15/mtd-spi-nor-sst-fix-write-enable-before-aai-sequence.patch @@ -0,0 +1,66 @@ +From stable+bounces-244833-greg=kroah.com@vger.kernel.org Sat May 9 02:48:22 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 17:18:16 -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: <20260508211816.1960968-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 +@@ -112,6 +112,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. */ +@@ -125,6 +127,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.15/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch b/queue-5.15/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch new file mode 100644 index 0000000000..310f9dd0fe --- /dev/null +++ b/queue-5.15/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch @@ -0,0 +1,176 @@ +From stable+bounces-243012-greg=kroah.com@vger.kernel.org Mon May 4 17:47:03 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 08:16:28 -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: <20260504121628.2165390-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-flag check `(dst->flags & (BR_PROXYARP_WIFI | BR_NEIGH_SUPPRESS))` and `cb->args[2]` indexing 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; + } +@@ -462,6 +466,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; +@@ -477,7 +482,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) { +@@ -489,8 +495,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)) +@@ -836,9 +842,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 +@@ -846,10 +854,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.15/net-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.patch b/queue-5.15/net-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.patch new file mode 100644 index 0000000000..f7820016d6 --- /dev/null +++ b/queue-5.15/net-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.patch @@ -0,0 +1,90 @@ +From stable+bounces-242960-greg=kroah.com@vger.kernel.org Mon May 4 14:34:05 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 04:59:28 -0400 +Subject: net: mctp: fix don't require received header reserved bits to be zero +To: stable@vger.kernel.org +Cc: Yuan Zhaoming , Jeremy Kerr , Jakub Kicinski , Sasha Levin +Message-ID: <20260504085928.1894173-1-sashal@kernel.org> + +From: Yuan Zhaoming + +[ Upstream commit a663bac71a2f0b3ac6c373168ca57b2a6e6381aa ] + +>From the MCTP Base specification (DSP0236 v1.2.1), the first byte of +the MCTP header contains a 4 bit reserved field, and 4 bit version. + +On our current receive path, we require those 4 reserved bits to be +zero, but the 9500-8i card is non-conformant, and may set these +reserved bits. + +DSP0236 states that the reserved bits must be written as zero, and +ignored when read. While the device might not conform to the former, +we should accept these message to conform to the latter. + +Relax our check on the MCTP version byte to allow non-zero bits in the +reserved field. + +Fixes: 889b7da23abf ("mctp: Add initial routing framework") +Signed-off-by: Yuan Zhaoming +Cc: stable@vger.kernel.org +Acked-by: Jeremy Kerr +Link: https://patch.msgid.link/20260417141340.5306-1-yuanzhaoming901030@126.com +Signed-off-by: Jakub Kicinski +[ Context ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/net/mctp.h | 3 +++ + net/mctp/route.c | 8 ++++++-- + 2 files changed, 9 insertions(+), 2 deletions(-) + +--- a/include/net/mctp.h ++++ b/include/net/mctp.h +@@ -25,6 +25,9 @@ struct mctp_hdr { + #define MCTP_VER_MIN 1 + #define MCTP_VER_MAX 1 + ++/* Definitions for ver field */ ++#define MCTP_HDR_VER_MASK GENMASK(3, 0) ++ + /* Definitions for flags_seq_tag field */ + #define MCTP_HDR_FLAG_SOM BIT(7) + #define MCTP_HDR_FLAG_EOM BIT(6) +--- a/net/mctp/route.c ++++ b/net/mctp/route.c +@@ -229,6 +229,7 @@ static int mctp_route_input(struct mctp_ + unsigned long f; + u8 tag, flags; + int rc; ++ u8 ver; + + msk = NULL; + rc = -EINVAL; +@@ -246,7 +247,8 @@ static int mctp_route_input(struct mctp_ + mh = mctp_hdr(skb); + skb_pull(skb, sizeof(struct mctp_hdr)); + +- if (mh->ver != 1) ++ ver = mh->ver & MCTP_HDR_VER_MASK; ++ if (ver < MCTP_VER_MIN || ver > MCTP_VER_MAX) + goto out; + + flags = mh->flags_seq_tag & (MCTP_HDR_FLAG_SOM | MCTP_HDR_FLAG_EOM); +@@ -859,6 +861,7 @@ static int mctp_pkttype_receive(struct s + struct mctp_skb_cb *cb; + struct mctp_route *rt; + struct mctp_hdr *mh; ++ u8 ver; + + /* basic non-data sanity checks */ + if (dev->type != ARPHRD_MCTP) +@@ -872,7 +875,8 @@ static int mctp_pkttype_receive(struct s + + /* We have enough for a header; decode and route */ + mh = mctp_hdr(skb); +- if (mh->ver < MCTP_VER_MIN || mh->ver > MCTP_VER_MAX) ++ ver = mh->ver & MCTP_HDR_VER_MASK; ++ if (ver < MCTP_VER_MIN || ver > MCTP_VER_MAX) + goto err_drop; + + cb = __mctp_cb(skb); diff --git a/queue-5.15/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch b/queue-5.15/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch new file mode 100644 index 0000000000..18b5e71274 --- /dev/null +++ b/queue-5.15/net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch @@ -0,0 +1,96 @@ +From stable+bounces-241120-greg=kroah.com@vger.kernel.org Sat Apr 25 15:31:39 2026 +From: Sasha Levin +Date: Sat, 25 Apr 2026 06:01:33 -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: <20260425100133.3487288-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 +@@ -2729,7 +2729,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; +@@ -2829,16 +2830,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, +@@ -2875,12 +2880,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.15/net-qrtr-ns-change-servers-radix-tree-to-xarray.patch b/queue-5.15/net-qrtr-ns-change-servers-radix-tree-to-xarray.patch new file mode 100644 index 0000000000..0434a61463 --- /dev/null +++ b/queue-5.15/net-qrtr-ns-change-servers-radix-tree-to-xarray.patch @@ -0,0 +1,310 @@ +From stable+bounces-242840-greg=kroah.com@vger.kernel.org Mon May 4 11:09:41 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 01:39:31 -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: <20260504053932.1748829-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; + } + +@@ -585,13 +527,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) +@@ -616,40 +557,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.15/net-qrtr-ns-free-the-node-during-ctrl_cmd_bye.patch b/queue-5.15/net-qrtr-ns-free-the-node-during-ctrl_cmd_bye.patch new file mode 100644 index 0000000000..5644c74c1a --- /dev/null +++ b/queue-5.15/net-qrtr-ns-free-the-node-during-ctrl_cmd_bye.patch @@ -0,0 +1,77 @@ +From stable+bounces-242841-greg=kroah.com@vger.kernel.org Mon May 4 11:09:43 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 01:39:32 -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: <20260504053932.1748829-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.15/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch b/queue-5.15/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch new file mode 100644 index 0000000000..faf0e46db0 --- /dev/null +++ b/queue-5.15/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch @@ -0,0 +1,95 @@ +From stable+bounces-242836-greg=kroah.com@vger.kernel.org Mon May 4 10:35:29 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 01:05:10 -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: <20260504050510.1612332-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 +@@ -590,6 +597,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; +@@ -598,6 +610,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; +@@ -664,6 +677,7 @@ static void ctrl_cmd_del_lookup(struct s + + list_del(&lookup->li); + kfree(lookup); ++ qrtr_ns.lookup_count--; + } + } + diff --git a/queue-5.15/net-qrtr-ns-limit-the-total-number-of-nodes.patch b/queue-5.15/net-qrtr-ns-limit-the-total-number-of-nodes.patch new file mode 100644 index 0000000000..eeaa9f6e57 --- /dev/null +++ b/queue-5.15/net-qrtr-ns-limit-the-total-number-of-nodes.patch @@ -0,0 +1,72 @@ +From stable+bounces-242992-greg=kroah.com@vger.kernel.org Mon May 4 16:06:33 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 06:32:53 -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: <20260504103253.2070381-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.15/net-tcp-md5-fix-mac-comparison-to-be-constant-time.patch b/queue-5.15/net-tcp-md5-fix-mac-comparison-to-be-constant-time.patch new file mode 100644 index 0000000000..a9680f7415 --- /dev/null +++ b/queue-5.15/net-tcp-md5-fix-mac-comparison-to-be-constant-time.patch @@ -0,0 +1,81 @@ +From 46d0d6f50dab706637f4c18a470aac20a21900d3 Mon Sep 17 00:00:00 2001 +From: Eric Biggers +Date: Mon, 2 Mar 2026 12:34:09 -0800 +Subject: net/tcp-md5: Fix MAC comparison to be constant-time + +From: Eric Biggers + +commit 46d0d6f50dab706637f4c18a470aac20a21900d3 upstream. + +To prevent timing attacks, MACs need to be compared in constant +time. Use the appropriate helper function for this. + +Fixes: cfb6eeb4c860 ("[TCP]: MD5 Signature Option (RFC2385) support.") +Fixes: 658ddaaf6694 ("tcp: md5: RST: getting md5 key from listener") +Cc: stable@vger.kernel.org +Signed-off-by: Eric Biggers +Link: https://patch.msgid.link/20260302203409.13388-1-ebiggers@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Li hongliang <1468888505@139.com> +Signed-off-by: Greg Kroah-Hartman +--- + net/ipv4/tcp_ipv4.c | 5 +++-- + net/ipv6/tcp_ipv6.c | 5 +++-- + 2 files changed, 6 insertions(+), 4 deletions(-) + +--- a/net/ipv4/tcp_ipv4.c ++++ b/net/ipv4/tcp_ipv4.c +@@ -78,6 +78,7 @@ + #include + #include + ++#include + #include + #include + +@@ -763,7 +764,7 @@ static void tcp_v4_send_reset(const stru + + + genhash = tcp_v4_md5_hash_skb(newhash, key, NULL, skb); +- if (genhash || memcmp(hash_location, newhash, 16) != 0) ++ if (genhash || crypto_memneq(hash_location, newhash, 16)) + goto out; + + } +@@ -1467,7 +1468,7 @@ static bool tcp_v4_inbound_md5_hash(cons + hash_expected, + NULL, skb); + +- if (genhash || memcmp(hash_location, newhash, 16) != 0) { ++ if (genhash || crypto_memneq(hash_location, newhash, 16)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE); + net_info_ratelimited("MD5 Hash failed for (%pI4, %d)->(%pI4, %d)%s L3 index %d\n", + &iph->saddr, ntohs(th->source), +--- a/net/ipv6/tcp_ipv6.c ++++ b/net/ipv6/tcp_ipv6.c +@@ -63,6 +63,7 @@ + #include + #include + ++#include + #include + #include + +@@ -810,7 +811,7 @@ static bool tcp_v6_inbound_md5_hash(cons + hash_expected, + NULL, skb); + +- if (genhash || memcmp(hash_location, newhash, 16) != 0) { ++ if (genhash || crypto_memneq(hash_location, newhash, 16)) { + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE); + net_info_ratelimited("MD5 Hash %s for [%pI6c]:%u->[%pI6c]:%u L3 index %d\n", + genhash ? "failed" : "mismatch", +@@ -1088,7 +1089,7 @@ static void tcp_v6_send_reset(const stru + goto out; + + genhash = tcp_v6_md5_hash_skb(newhash, key, NULL, skb); +- if (genhash || memcmp(hash_location, newhash, 16) != 0) ++ if (genhash || crypto_memneq(hash_location, newhash, 16)) + goto out; + } + #endif diff --git a/queue-5.15/nvme-fix-interpretation-of-dmrsl.patch b/queue-5.15/nvme-fix-interpretation-of-dmrsl.patch new file mode 100644 index 0000000000..dc0d9e5247 --- /dev/null +++ b/queue-5.15/nvme-fix-interpretation-of-dmrsl.patch @@ -0,0 +1,57 @@ +From stable+bounces-242557-greg=kroah.com@vger.kernel.org Sat May 2 04:58:14 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 19:27:43 -0400 +Subject: nvme: fix interpretation of DMRSL +To: stable@vger.kernel.org +Cc: Tom Yan , Christoph Hellwig , Sasha Levin +Message-ID: <20260501232744.4102493-1-sashal@kernel.org> + +From: Tom Yan + +[ Upstream commit 1a86924e4f464757546d7f7bdc469be237918395 ] + +DMRSLl is in the unit of logical blocks, while max_discard_sectors is +in the unit of "linux sector". + +Signed-off-by: Tom Yan +Signed-off-by: Christoph Hellwig +Stable-dep-of: 40f0496b617b ("nvme: respect NVME_QUIRK_DISABLE_WRITE_ZEROES when wzsl is set") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvme/host/core.c | 6 ++++-- + drivers/nvme/host/nvme.h | 1 + + 2 files changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/nvme/host/core.c ++++ b/drivers/nvme/host/core.c +@@ -1736,6 +1736,9 @@ static void nvme_config_discard(struct g + if (blk_queue_flag_test_and_set(QUEUE_FLAG_DISCARD, queue)) + return; + ++ if (ctrl->dmrsl && ctrl->dmrsl <= nvme_sect_to_lba(ns, UINT_MAX)) ++ ctrl->max_discard_sectors = nvme_lba_to_sect(ns, ctrl->dmrsl); ++ + blk_queue_max_discard_sectors(queue, ctrl->max_discard_sectors); + blk_queue_max_discard_segments(queue, ctrl->max_discard_segments); + +@@ -2948,8 +2951,7 @@ static int nvme_init_non_mdts_limits(str + + if (id->dmrl) + ctrl->max_discard_segments = id->dmrl; +- if (id->dmrsl) +- ctrl->max_discard_sectors = le32_to_cpu(id->dmrsl); ++ ctrl->dmrsl = le32_to_cpu(id->dmrsl); + if (id->wzsl) + ctrl->max_zeroes_sectors = nvme_mps_to_sectors(ctrl, id->wzsl); + +--- a/drivers/nvme/host/nvme.h ++++ b/drivers/nvme/host/nvme.h +@@ -299,6 +299,7 @@ struct nvme_ctrl { + #endif + u16 crdt[3]; + u16 oncs; ++ u32 dmrsl; + u16 oacs; + u16 nssa; + u16 nr_streams; diff --git a/queue-5.15/nvme-respect-nvme_quirk_disable_write_zeroes-when-wzsl-is-set.patch b/queue-5.15/nvme-respect-nvme_quirk_disable_write_zeroes-when-wzsl-is-set.patch new file mode 100644 index 0000000000..e3f5fd6b83 --- /dev/null +++ b/queue-5.15/nvme-respect-nvme_quirk_disable_write_zeroes-when-wzsl-is-set.patch @@ -0,0 +1,45 @@ +From stable+bounces-242556-greg=kroah.com@vger.kernel.org Sat May 2 04:58:05 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 19:27:44 -0400 +Subject: nvme: respect NVME_QUIRK_DISABLE_WRITE_ZEROES when wzsl is set +To: stable@vger.kernel.org +Cc: Robert Beckett , Keith Busch , Sasha Levin +Message-ID: <20260501232744.4102493-2-sashal@kernel.org> + +From: Robert Beckett + +[ Upstream commit 40f0496b617b431f8d2dd94d7f785c1121f8a68a ] + +The NVM Command Set Identify Controller data may report a non-zero +Write Zeroes Size Limit (wzsl). When present, nvme_init_non_mdts_limits() +unconditionally overrides max_zeroes_sectors from wzsl, even if +NVME_QUIRK_DISABLE_WRITE_ZEROES previously set it to zero. + +This effectively re-enables write zeroes for devices that need it +disabled, defeating the quirk. Several Kingston OM* drives rely on +this quirk to avoid firmware issues with write zeroes commands. + +Check for the quirk before applying the wzsl override. + +Fixes: 5befc7c26e5a ("nvme: implement non-mdts command limits") +Cc: stable@vger.kernel.org +Signed-off-by: Robert Beckett +Assisted-by: claude-opus-4-6-v1 +Signed-off-by: Keith Busch +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/nvme/host/core.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/nvme/host/core.c ++++ b/drivers/nvme/host/core.c +@@ -2952,7 +2952,7 @@ static int nvme_init_non_mdts_limits(str + if (id->dmrl) + ctrl->max_discard_segments = id->dmrl; + ctrl->dmrsl = le32_to_cpu(id->dmrsl); +- if (id->wzsl) ++ if (id->wzsl && !(ctrl->quirks & NVME_QUIRK_DISABLE_WRITE_ZEROES)) + ctrl->max_zeroes_sectors = nvme_mps_to_sectors(ctrl, id->wzsl); + + free_data: diff --git a/queue-5.15/printk-add-print_hex_dump_devel.patch b/queue-5.15/printk-add-print_hex_dump_devel.patch new file mode 100644 index 0000000000..e3f4df0300 --- /dev/null +++ b/queue-5.15/printk-add-print_hex_dump_devel.patch @@ -0,0 +1,49 @@ +From stable+bounces-245030-greg=kroah.com@vger.kernel.org Sun May 10 20:40:18 2026 +From: Sasha Levin +Date: Sun, 10 May 2026 11:10:09 -0400 +Subject: printk: add print_hex_dump_devel() +To: stable@vger.kernel.org +Cc: Thorsten Blum , Herbert Xu , John Ogness , Sasha Levin +Message-ID: <20260510151010.38344-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 +@@ -740,6 +740,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.15/randomize_kstack-maintain-kstack_offset-per-task.patch b/queue-5.15/randomize_kstack-maintain-kstack_offset-per-task.patch new file mode 100644 index 0000000000..ccac32cc53 --- /dev/null +++ b/queue-5.15/randomize_kstack-maintain-kstack_offset-per-task.patch @@ -0,0 +1,168 @@ +From stable+bounces-244038-greg=kroah.com@vger.kernel.org Tue May 5 15:22:40 2026 +From: Sasha Levin +Date: Tue, 5 May 2026 05:49:40 -0400 +Subject: randomize_kstack: Maintain kstack_offset per task +To: stable@vger.kernel.org +Cc: Ryan Roberts , Mark Rutland , Kees Cook , Sasha Levin +Message-ID: <20260505094940.506135-1-sashal@kernel.org> + +From: Ryan Roberts + +[ Upstream commit 37beb42560165869838e7d91724f3e629db64129 ] + +kstack_offset was previously maintained per-cpu, but this caused a +couple of issues. So let's instead make it per-task. + +Issue 1: add_random_kstack_offset() and choose_random_kstack_offset() +expected and required to be called with interrupts and preemption +disabled so that it could manipulate per-cpu state. But arm64, loongarch +and risc-v are calling them with interrupts and preemption enabled. I +don't _think_ this causes any functional issues, but it's certainly +unexpected and could lead to manipulating the wrong cpu's state, which +could cause a minor performance degradation due to bouncing the cache +lines. By maintaining the state per-task those functions can safely be +called in preemptible context. + +Issue 2: add_random_kstack_offset() is called before executing the +syscall and expands the stack using a previously chosen random offset. +choose_random_kstack_offset() is called after executing the syscall and +chooses and stores a new random offset for the next syscall. With +per-cpu storage for this offset, an attacker could force cpu migration +during the execution of the syscall and prevent the offset from being +updated for the original cpu such that it is predictable for the next +syscall on that cpu. By maintaining the state per-task, this problem +goes away because the per-task random offset is updated after the +syscall regardless of which cpu it is executing on. + +Fixes: 39218ff4c625 ("stack: Optionally randomize kernel stack offset each syscall") +Closes: https://lore.kernel.org/all/dd8c37bc-795f-4c7a-9086-69e584d8ab24@arm.com/ +Cc: stable@vger.kernel.org +Acked-by: Mark Rutland +Signed-off-by: Ryan Roberts +Link: https://patch.msgid.link/20260303150840.3789438-2-ryan.roberts@arm.com +Signed-off-by: Kees Cook +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/randomize_kstack.h | 44 +++++++++++++++++++++++++++++++-------- + include/linux/sched.h | 4 +++ + init/main.c | 1 + kernel/fork.c | 2 + + 4 files changed, 42 insertions(+), 9 deletions(-) + +--- a/include/linux/randomize_kstack.h ++++ b/include/linux/randomize_kstack.h +@@ -8,7 +8,6 @@ + + DECLARE_STATIC_KEY_MAYBE(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, + randomize_kstack_offset); +-DECLARE_PER_CPU(u32, kstack_offset); + + /* + * Do not use this anywhere else in the kernel. This is used here because +@@ -39,28 +38,57 @@ DECLARE_PER_CPU(u32, kstack_offset); + */ + #define KSTACK_OFFSET_MAX(x) ((x) & 0x3FF) + +-/* +- * These macros must be used during syscall entry when interrupts and +- * preempt are disabled, and after user registers have been stored to +- * the stack. ++/** ++ * add_random_kstack_offset - Increase stack utilization by previously ++ * chosen random offset ++ * ++ * This should be used in the syscall entry path after user registers have been ++ * stored to the stack. Preemption may be enabled. For testing the resulting ++ * entropy, please see: tools/testing/selftests/lkdtm/stack-entropy.sh + */ + #define add_random_kstack_offset() do { \ + if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \ + &randomize_kstack_offset)) { \ +- u32 offset = raw_cpu_read(kstack_offset); \ ++ u32 offset = current->kstack_offset; \ + u8 *ptr = __kstack_alloca(KSTACK_OFFSET_MAX(offset)); \ + /* Keep allocation even after "ptr" loses scope. */ \ + asm volatile("" :: "r"(ptr) : "memory"); \ + } \ + } while (0) + ++/** ++ * choose_random_kstack_offset - Choose the random offset for the next ++ * add_random_kstack_offset() ++ * ++ * This should only be used during syscall exit. Preemption may be enabled. This ++ * position in the syscall flow is done to frustrate attacks from userspace ++ * attempting to learn the next offset: ++ * - Maximize the timing uncertainty visible from userspace: if the ++ * offset is chosen at syscall entry, userspace has much more control ++ * over the timing between choosing offsets. "How long will we be in ++ * kernel mode?" tends to be more difficult to predict than "how long ++ * will we be in user mode?" ++ * - Reduce the lifetime of the new offset sitting in memory during ++ * kernel mode execution. Exposure of "thread-local" memory content ++ * (e.g. current, percpu, etc) tends to be easier than arbitrary ++ * location memory exposure. ++ */ + #define choose_random_kstack_offset(rand) do { \ + if (static_branch_maybe(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, \ + &randomize_kstack_offset)) { \ +- u32 offset = raw_cpu_read(kstack_offset); \ ++ u32 offset = current->kstack_offset; \ + offset = ror32(offset, 5) ^ (rand); \ +- raw_cpu_write(kstack_offset, offset); \ ++ current->kstack_offset = offset; \ + } \ + } while (0) + ++#ifdef CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET ++static inline void random_kstack_task_init(struct task_struct *tsk) ++{ ++ tsk->kstack_offset = 0; ++} ++#else ++#define random_kstack_task_init(tsk) do { } while (0) ++#endif ++ + #endif +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -1461,6 +1461,10 @@ struct task_struct { + unsigned long prev_lowest_stack; + #endif + ++#ifdef CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET ++ u32 kstack_offset; ++#endif ++ + #ifdef CONFIG_X86_MCE + void __user *mce_vaddr; + __u64 mce_kflags; +--- a/init/main.c ++++ b/init/main.c +@@ -882,7 +882,6 @@ static void __init mm_init(void) + #ifdef CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET + DEFINE_STATIC_KEY_MAYBE_RO(CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT, + randomize_kstack_offset); +-DEFINE_PER_CPU(u32, kstack_offset); + + static int __init early_randomize_kstack_offset(char *buf) + { +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -94,6 +94,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2300,6 +2301,7 @@ static __latent_entropy struct task_stru + if (retval) + goto bad_fork_cleanup_io; + ++ random_kstack_task_init(p); + stackleak_task_init(p); + + if (pid != &init_struct_pid) { diff --git a/queue-5.15/rtw88-8821ce-disable-pcie-aspm-l1-for-8821ce-using-chip-id.patch b/queue-5.15/rtw88-8821ce-disable-pcie-aspm-l1-for-8821ce-using-chip-id.patch new file mode 100644 index 0000000000..d25c705d1d --- /dev/null +++ b/queue-5.15/rtw88-8821ce-disable-pcie-aspm-l1-for-8821ce-using-chip-id.patch @@ -0,0 +1,36 @@ +From stable+bounces-242156-greg=kroah.com@vger.kernel.org Thu Apr 30 21:44:39 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 12:07:39 -0400 +Subject: rtw88: 8821ce: Disable PCIe ASPM L1 for 8821CE using chip ID +To: stable@vger.kernel.org +Cc: Jimmy Hon , Ping-Ke Shih , Kalle Valo , Sasha Levin +Message-ID: <20260430160740.1785374-1-sashal@kernel.org> + +From: Jimmy Hon + +[ Upstream commit b9eb5f0742d107ca9c0f33da58f61ce83e3ce2fc ] + +Make workaround work for other 8821CE devices with different PCI ID + +Signed-off-by: Jimmy Hon +Reviewed-by: Ping-Ke Shih +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20220407075123.420696-3-honyuenkwun@gmail.com +Stable-dep-of: eb101d2abdcc ("wifi: rtw88: check for PCI upstream bridge existence") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/realtek/rtw88/pci.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/realtek/rtw88/pci.c ++++ b/drivers/net/wireless/realtek/rtw88/pci.c +@@ -1766,7 +1766,7 @@ int rtw_pci_probe(struct pci_dev *pdev, + } + + /* Disable PCIe ASPM L1 while doing NAPI poll for 8821CE */ +- if (pdev->device == 0xc821 && bridge->vendor == PCI_VENDOR_ID_INTEL) ++ if (rtwdev->chip->id == RTW_CHIP_TYPE_8821C && bridge->vendor == PCI_VENDOR_ID_INTEL) + rtwpci->rx_no_aspm = true; + + rtw_pci_phy_cfg(rtwdev); diff --git a/queue-5.15/rxrpc-fix-conn-level-packet-handling-to-unshare-response-packets.patch b/queue-5.15/rxrpc-fix-conn-level-packet-handling-to-unshare-response-packets.patch new file mode 100644 index 0000000000..70d91f1421 --- /dev/null +++ b/queue-5.15/rxrpc-fix-conn-level-packet-handling-to-unshare-response-packets.patch @@ -0,0 +1,88 @@ +From stable+bounces-242815-greg=kroah.com@vger.kernel.org Mon May 4 00:44:24 2026 +From: Sasha Levin +Date: Sun, 3 May 2026 15:14:16 -0400 +Subject: rxrpc: Fix conn-level packet handling to unshare RESPONSE packets +To: stable@vger.kernel.org +Cc: David Howells , Marc Dionne , Jeffrey Altman , Simon Horman , linux-afs@lists.infradead.org, stable@kernel.org, Jakub Kicinski , Sasha Levin +Message-ID: <20260503191416.1286222-1-sashal@kernel.org> + +From: David Howells + +[ Upstream commit 24481a7f573305706054c59e275371f8d0fe919f ] + +The security operations that verify the RESPONSE packets decrypt bits of it +in place - however, the sk_buff may be shared with a packet sniffer, which +would lead to the sniffer seeing an apparently corrupt packet (actually +decrypted). + +Fix this by handing a copy of the packet off to the specific security +handler if the packet was cloned. + +Fixes: 17926a79320a ("[AF_RXRPC]: Provide secure RxRPC sockets for use by userspace and kernel both") +Closes: https://sashiko.dev/#/patchset/20260408121252.2249051-1-dhowells%40redhat.com +Signed-off-by: David Howells +cc: Marc Dionne +cc: Jeffrey Altman +cc: Simon Horman +cc: linux-afs@lists.infradead.org +cc: stable@kernel.org +Link: https://patch.msgid.link/20260422161438.2593376-5-dhowells@redhat.com +Signed-off-by: Jakub Kicinski +[ adapted callback signature to include `_abort_code` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/rxrpc/conn_event.c | 30 +++++++++++++++++++++++++++++- + 1 file changed, 29 insertions(+), 1 deletion(-) + +diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c +index 5d91ef562ff78..09438850f9a5a 100644 +--- a/net/rxrpc/conn_event.c ++++ b/net/rxrpc/conn_event.c +@@ -285,6 +285,34 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call) + } + } + ++static int rxrpc_verify_response(struct rxrpc_connection *conn, ++ struct sk_buff *skb, ++ u32 *_abort_code) ++{ ++ int ret; ++ ++ if (skb_cloned(skb)) { ++ /* Copy the packet if shared so that we can do in-place ++ * decryption. ++ */ ++ struct sk_buff *nskb = skb_copy(skb, GFP_NOFS); ++ ++ if (nskb) { ++ rxrpc_new_skb(nskb, rxrpc_skb_unshared); ++ ret = conn->security->verify_response(conn, nskb, _abort_code); ++ rxrpc_free_skb(nskb, rxrpc_skb_freed); ++ } else { ++ /* OOM - Drop the packet. */ ++ rxrpc_see_skb(skb, rxrpc_skb_unshared_nomem); ++ ret = -ENOMEM; ++ } ++ } else { ++ ret = conn->security->verify_response(conn, skb, _abort_code); ++ } ++ ++ return ret; ++} ++ + /* + * connection-level Rx packet processor + */ +@@ -337,7 +365,7 @@ static int rxrpc_process_event(struct rxrpc_connection *conn, + _abort_code); + + case RXRPC_PACKET_TYPE_RESPONSE: +- ret = conn->security->verify_response(conn, skb, _abort_code); ++ ret = rxrpc_verify_response(conn, skb, _abort_code); + if (ret < 0) + return ret; + +-- +2.53.0 + diff --git a/queue-5.15/sched-use-u64-for-bandwidth-ratio-calculations.patch b/queue-5.15/sched-use-u64-for-bandwidth-ratio-calculations.patch new file mode 100644 index 0000000000..bfaecc4432 --- /dev/null +++ b/queue-5.15/sched-use-u64-for-bandwidth-ratio-calculations.patch @@ -0,0 +1,74 @@ +From stable+bounces-242628-greg=kroah.com@vger.kernel.org Sun May 3 05:59:34 2026 +From: Sasha Levin +Date: Sat, 2 May 2026 20:26:45 -0400 +Subject: sched: Use u64 for bandwidth ratio calculations +To: stable@vger.kernel.org +Cc: Joseph Salisbury , "Peter Zijlstra (Intel)" , Sasha Levin +Message-ID: <20260503002645.929471-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 +@@ -4491,7 +4491,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 +@@ -2606,7 +2606,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 +@@ -2340,7 +2340,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.15/scsi-core-pm-rely-on-the-device-driver-core-for-async-power-management.patch b/queue-5.15/scsi-core-pm-rely-on-the-device-driver-core-for-async-power-management.patch new file mode 100644 index 0000000000..651f8479b1 --- /dev/null +++ b/queue-5.15/scsi-core-pm-rely-on-the-device-driver-core-for-async-power-management.patch @@ -0,0 +1,201 @@ +From stable+bounces-244970-greg=kroah.com@vger.kernel.org Sat May 9 21:38:57 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 12:08:46 -0400 +Subject: scsi: core: pm: Rely on the device driver core for async power management +To: stable@vger.kernel.org +Cc: Bart Van Assche , Alan Stern , Dan Williams , Hannes Reinecke , Adrian Hunter , Martin Kepplinger , "Martin K. Petersen" , Sasha Levin +Message-ID: <20260509160849.3584738-1-sashal@kernel.org> + +From: Bart Van Assche + +[ Upstream commit a19a93e4c6a98c9c0f2f5a6db76846f10d7d1f85 ] + +Instead of implementing asynchronous resume support in the SCSI core, rely +on the device driver core for resuming SCSI devices asynchronously. +Instead of only supporting asynchronous resumes, also support asynchronous +suspends. + +Link: https://lore.kernel.org/r/20211006215453.3318929-2-bvanassche@acm.org +Cc: Alan Stern +Cc: Dan Williams +Cc: Hannes Reinecke +Cc: Adrian Hunter +Cc: Martin Kepplinger +Signed-off-by: Bart Van Assche +Signed-off-by: Martin K. Petersen +Stable-dep-of: 1e111c4b3a72 ("scsi: sd: fix missing put_disk() when device_add(&disk_dev) fails") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/scsi/hosts.c | 1 + + drivers/scsi/scsi.c | 8 -------- + drivers/scsi/scsi_pm.c | 44 ++------------------------------------------ + drivers/scsi/scsi_priv.h | 4 +--- + drivers/scsi/scsi_scan.c | 17 +++++++++++++++++ + drivers/scsi/scsi_sysfs.c | 1 + + drivers/scsi/sd.c | 1 - + 7 files changed, 22 insertions(+), 54 deletions(-) + +--- a/drivers/scsi/hosts.c ++++ b/drivers/scsi/hosts.c +@@ -487,6 +487,7 @@ struct Scsi_Host *scsi_host_alloc(struct + dev_set_name(&shost->shost_gendev, "host%d", shost->host_no); + shost->shost_gendev.bus = &scsi_bus_type; + shost->shost_gendev.type = &scsi_host_type; ++ scsi_enable_async_suspend(&shost->shost_gendev); + + device_initialize(&shost->shost_dev); + shost->shost_dev.parent = &shost->shost_gendev; +--- a/drivers/scsi/scsi.c ++++ b/drivers/scsi/scsi.c +@@ -86,14 +86,6 @@ unsigned int scsi_logging_level; + EXPORT_SYMBOL(scsi_logging_level); + #endif + +-/* +- * Domain for asynchronous system resume operations. It is marked 'exclusive' +- * to avoid being included in the async_synchronize_full() that is invoked by +- * dpm_resume(). +- */ +-ASYNC_DOMAIN_EXCLUSIVE(scsi_sd_pm_domain); +-EXPORT_SYMBOL(scsi_sd_pm_domain); +- + #ifdef CONFIG_SCSI_LOGGING + void scsi_log_send(struct scsi_cmnd *cmd) + { +--- a/drivers/scsi/scsi_pm.c ++++ b/drivers/scsi/scsi_pm.c +@@ -56,9 +56,6 @@ static int scsi_dev_type_suspend(struct + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + int err; + +- /* flush pending in-flight resume operations, suspend is synchronous */ +- async_synchronize_full_domain(&scsi_sd_pm_domain); +- + err = scsi_device_quiesce(to_scsi_device(dev)); + if (err == 0) { + err = cb(dev, pm); +@@ -123,48 +120,11 @@ scsi_bus_suspend_common(struct device *d + return err; + } + +-static void async_sdev_resume(void *dev, async_cookie_t cookie) +-{ +- scsi_dev_type_resume(dev, do_scsi_resume); +-} +- +-static void async_sdev_thaw(void *dev, async_cookie_t cookie) +-{ +- scsi_dev_type_resume(dev, do_scsi_thaw); +-} +- +-static void async_sdev_restore(void *dev, async_cookie_t cookie) +-{ +- scsi_dev_type_resume(dev, do_scsi_restore); +-} +- + static int scsi_bus_resume_common(struct device *dev, + int (*cb)(struct device *, const struct dev_pm_ops *)) + { +- async_func_t fn; +- +- if (!scsi_is_sdev_device(dev)) +- fn = NULL; +- else if (cb == do_scsi_resume) +- fn = async_sdev_resume; +- else if (cb == do_scsi_thaw) +- fn = async_sdev_thaw; +- else if (cb == do_scsi_restore) +- fn = async_sdev_restore; +- else +- fn = NULL; +- +- if (fn) { +- async_schedule_domain(fn, dev, &scsi_sd_pm_domain); +- +- /* +- * If a user has disabled async probing a likely reason +- * is due to a storage enclosure that does not inject +- * staggered spin-ups. For safety, make resume +- * synchronous as well in that case. +- */ +- if (strncmp(scsi_scan_type, "async", 5) != 0) +- async_synchronize_full_domain(&scsi_sd_pm_domain); ++ if (scsi_is_sdev_device(dev)) { ++ scsi_dev_type_resume(dev, cb); + } else { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); +--- a/drivers/scsi/scsi_priv.h ++++ b/drivers/scsi/scsi_priv.h +@@ -117,7 +117,7 @@ extern void scsi_exit_procfs(void); + #endif /* CONFIG_PROC_FS */ + + /* scsi_scan.c */ +-extern char scsi_scan_type[]; ++void scsi_enable_async_suspend(struct device *dev); + extern int scsi_complete_async_scans(void); + extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int, + unsigned int, u64, enum scsi_scan_mode); +@@ -171,8 +171,6 @@ static inline int scsi_autopm_get_host(s + static inline void scsi_autopm_put_host(struct Scsi_Host *h) {} + #endif /* CONFIG_PM */ + +-extern struct async_domain scsi_sd_pm_domain; +- + /* scsi_dh.c */ + #ifdef CONFIG_SCSI_DH + void scsi_dh_add_device(struct scsi_device *sdev); +--- a/drivers/scsi/scsi_scan.c ++++ b/drivers/scsi/scsi_scan.c +@@ -123,6 +123,22 @@ struct async_scan_data { + }; + + /** ++ * scsi_enable_async_suspend - Enable async suspend and resume ++ */ ++void scsi_enable_async_suspend(struct device *dev) ++{ ++ /* ++ * If a user has disabled async probing a likely reason is due to a ++ * storage enclosure that does not inject staggered spin-ups. For ++ * safety, make resume synchronous as well in that case. ++ */ ++ if (strncmp(scsi_scan_type, "async", 5) != 0) ++ return; ++ /* Enable asynchronous suspend and resume. */ ++ device_enable_async_suspend(dev); ++} ++ ++/** + * scsi_complete_async_scans - Wait for asynchronous scans to complete + * + * When this function returns, any host which started scanning before +@@ -499,6 +515,7 @@ static struct scsi_target *scsi_alloc_ta + dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id); + dev->bus = &scsi_bus_type; + dev->type = &scsi_target_type; ++ scsi_enable_async_suspend(dev); + starget->id = id; + starget->channel = channel; + starget->can_queue = 0; +--- a/drivers/scsi/scsi_sysfs.c ++++ b/drivers/scsi/scsi_sysfs.c +@@ -1643,6 +1643,7 @@ void scsi_sysfs_device_initialize(struct + device_initialize(&sdev->sdev_gendev); + sdev->sdev_gendev.bus = &scsi_bus_type; + sdev->sdev_gendev.type = &scsi_dev_type; ++ scsi_enable_async_suspend(&sdev->sdev_gendev); + dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%llu", + sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); + +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -3505,7 +3505,6 @@ static int sd_remove(struct device *dev) + sdkp = dev_get_drvdata(dev); + scsi_autopm_get_device(sdkp->device); + +- async_synchronize_full_domain(&scsi_sd_pm_domain); + device_del(&sdkp->dev); + del_gendisk(sdkp->disk); + sd_shutdown(dev); diff --git a/queue-5.15/scsi-sd-add-error-handling-support-for-add_disk.patch b/queue-5.15/scsi-sd-add-error-handling-support-for-add_disk.patch new file mode 100644 index 0000000000..2d19552d0a --- /dev/null +++ b/queue-5.15/scsi-sd-add-error-handling-support-for-add_disk.patch @@ -0,0 +1,47 @@ +From stable+bounces-244971-greg=kroah.com@vger.kernel.org Sat May 9 21:38:58 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 12:08:47 -0400 +Subject: scsi: sd: Add error handling support for add_disk() +To: stable@vger.kernel.org +Cc: Luis Chamberlain , Christoph Hellwig , "Martin K. Petersen" , Sasha Levin +Message-ID: <20260509160849.3584738-2-sashal@kernel.org> + +From: Luis Chamberlain + +[ Upstream commit 2a7a891f4c406822801ecd676b076c64de072c9e ] + +We never checked for errors on add_disk() as this function returned +void. Now that this is fixed, use the shiny new error handling. + +As with the error handling for device_add() we follow the same logic and +just put the device so that cleanup is done via the scsi_disk_release(). + +Link: https://lore.kernel.org/r/20211015233028.2167651-2-mcgrof@kernel.org +Reviewed-by: Christoph Hellwig +Acked-by: Martin K. Petersen +Signed-off-by: Luis Chamberlain +Signed-off-by: Martin K. Petersen +Stable-dep-of: 1e111c4b3a72 ("scsi: sd: fix missing put_disk() when device_add(&disk_dev) fails") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/scsi/sd.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -3458,7 +3458,13 @@ static int sd_probe(struct device *dev) + pm_runtime_set_autosuspend_delay(dev, + sdp->host->hostt->rpm_autosuspend_delay); + } +- device_add_disk(dev, gd, NULL); ++ ++ error = device_add_disk(dev, gd, NULL); ++ if (error) { ++ put_device(&sdkp->dev); ++ goto out; ++ } ++ + if (sdkp->capacity) + sd_dif_config_host(sdkp); + diff --git a/queue-5.15/scsi-sd-fix-missing-put_disk-when-device_add-disk_dev-fails.patch b/queue-5.15/scsi-sd-fix-missing-put_disk-when-device_add-disk_dev-fails.patch new file mode 100644 index 0000000000..22f3e283b6 --- /dev/null +++ b/queue-5.15/scsi-sd-fix-missing-put_disk-when-device_add-disk_dev-fails.patch @@ -0,0 +1,39 @@ +From stable+bounces-244973-greg=kroah.com@vger.kernel.org Sat May 9 21:39:04 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 12:08:49 -0400 +Subject: scsi: sd: fix missing put_disk() when device_add(&disk_dev) fails +To: stable@vger.kernel.org +Cc: Yang Xiuwei , John Garry , "Martin K. Petersen" , Sasha Levin +Message-ID: <20260509160849.3584738-4-sashal@kernel.org> + +From: Yang Xiuwei + +[ Upstream commit 1e111c4b3a726df1254670a5cc4868cedb946d37 ] + +If device_add(&sdkp->disk_dev) fails, put_device() runs +scsi_disk_release(), which frees the scsi_disk but leaves the gendisk +referenced. The device_add_disk() error path in sd_probe() calls +put_disk(gd); call put_disk(gd) here to mirror that cleanup. + +Fixes: 265dfe8ebbab ("scsi: sd: Free scsi_disk device via put_device()") +Cc: stable@vger.kernel.org +Reviewed-by: John Garry +Signed-off-by: Yang Xiuwei +Link: https://patch.msgid.link/20260330014952.152776-1-yangxiuwei@kylinos.cn +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/scsi/sd.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -3420,6 +3420,7 @@ static int sd_probe(struct device *dev) + error = device_add(&sdkp->disk_dev); + if (error) { + put_device(&sdkp->disk_dev); ++ put_disk(gd); + goto out; + } + diff --git a/queue-5.15/sd-rename-the-scsi_disk.dev-field.patch b/queue-5.15/sd-rename-the-scsi_disk.dev-field.patch new file mode 100644 index 0000000000..923ceef9f6 --- /dev/null +++ b/queue-5.15/sd-rename-the-scsi_disk.dev-field.patch @@ -0,0 +1,123 @@ +From stable+bounces-244972-greg=kroah.com@vger.kernel.org Sat May 9 21:39:01 2026 +From: Sasha Levin +Date: Sat, 9 May 2026 12:08:48 -0400 +Subject: sd: rename the scsi_disk.dev field +To: stable@vger.kernel.org +Cc: Christoph Hellwig , Bart Van Assche , Chaitanya Kulkarni , Ming Lei , "Martin K. Petersen" , Jens Axboe , Sasha Levin +Message-ID: <20260509160849.3584738-3-sashal@kernel.org> + +From: Christoph Hellwig + +[ Upstream commit fad45c3007a18064da759b4dba35eb722bc64e97 ] + +dev is very hard to grep for. Give the field a more descriptive name and +documents its purpose. + +Signed-off-by: Christoph Hellwig +Reviewed-by: Bart Van Assche +Reviewed-by: Chaitanya Kulkarni +Reviewed-by: Ming Lei +Reviewed-by: Martin K. Petersen +Link: https://lore.kernel.org/r/20220308055200.735835-5-hch@lst.de +Signed-off-by: Jens Axboe +Stable-dep-of: 1e111c4b3a72 ("scsi: sd: fix missing put_disk() when device_add(&disk_dev) fails") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/scsi/sd.c | 22 +++++++++++----------- + drivers/scsi/sd.h | 9 +++++++-- + 2 files changed, 18 insertions(+), 13 deletions(-) + +--- a/drivers/scsi/sd.c ++++ b/drivers/scsi/sd.c +@@ -674,7 +674,7 @@ static struct scsi_disk *scsi_disk_get(s + if (disk->private_data) { + sdkp = scsi_disk(disk); + if (scsi_device_get(sdkp->device) == 0) +- get_device(&sdkp->dev); ++ get_device(&sdkp->disk_dev); + else + sdkp = NULL; + } +@@ -687,7 +687,7 @@ static void scsi_disk_put(struct scsi_di + struct scsi_device *sdev = sdkp->device; + + mutex_lock(&sd_ref_mutex); +- put_device(&sdkp->dev); ++ put_device(&sdkp->disk_dev); + scsi_device_put(sdev); + mutex_unlock(&sd_ref_mutex); + } +@@ -3412,14 +3412,14 @@ static int sd_probe(struct device *dev) + SD_MOD_TIMEOUT); + } + +- device_initialize(&sdkp->dev); +- sdkp->dev.parent = get_device(dev); +- sdkp->dev.class = &sd_disk_class; +- dev_set_name(&sdkp->dev, "%s", dev_name(dev)); ++ device_initialize(&sdkp->disk_dev); ++ sdkp->disk_dev.parent = get_device(dev); ++ sdkp->disk_dev.class = &sd_disk_class; ++ dev_set_name(&sdkp->disk_dev, "%s", dev_name(dev)); + +- error = device_add(&sdkp->dev); ++ error = device_add(&sdkp->disk_dev); + if (error) { +- put_device(&sdkp->dev); ++ put_device(&sdkp->disk_dev); + goto out; + } + +@@ -3461,7 +3461,7 @@ static int sd_probe(struct device *dev) + + error = device_add_disk(dev, gd, NULL); + if (error) { +- put_device(&sdkp->dev); ++ put_device(&sdkp->disk_dev); + goto out; + } + +@@ -3511,7 +3511,7 @@ static int sd_remove(struct device *dev) + sdkp = dev_get_drvdata(dev); + scsi_autopm_get_device(sdkp->device); + +- device_del(&sdkp->dev); ++ device_del(&sdkp->disk_dev); + del_gendisk(sdkp->disk); + sd_shutdown(dev); + +@@ -3519,7 +3519,7 @@ static int sd_remove(struct device *dev) + + mutex_lock(&sd_ref_mutex); + dev_set_drvdata(dev, NULL); +- put_device(&sdkp->dev); ++ put_device(&sdkp->disk_dev); + mutex_unlock(&sd_ref_mutex); + + return 0; +--- a/drivers/scsi/sd.h ++++ b/drivers/scsi/sd.h +@@ -70,7 +70,12 @@ enum { + struct scsi_disk { + struct scsi_driver *driver; /* always &sd_template */ + struct scsi_device *device; +- struct device dev; ++ ++ /* ++ * disk_dev is used to show attributes in /sys/class/scsi_disk/, ++ * but otherwise not really needed. Do not use for refcounting. ++ */ ++ struct device disk_dev; + struct gendisk *disk; + struct opal_dev *opal_dev; + #ifdef CONFIG_BLK_DEV_ZONED +@@ -126,7 +131,7 @@ struct scsi_disk { + unsigned security : 1; + unsigned ignore_medium_access_errors : 1; + }; +-#define to_scsi_disk(obj) container_of(obj,struct scsi_disk,dev) ++#define to_scsi_disk(obj) container_of(obj, struct scsi_disk, disk_dev) + + static inline struct scsi_disk *scsi_disk(struct gendisk *disk) + { diff --git a/queue-5.15/series b/queue-5.15/series index 692b3964d5..8bccfa5c68 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -245,3 +245,62 @@ drm-amd-display-fix-null-deref-and-buffer-over-read-in-sdp-debugfs.patch drm-amd-display-use-krealloc_array-in-dal_vector_reserve.patch fs-fcntl-fix-softirq-unsafe-lock-order-in-fasync-signaling.patch mm-damon-ops-common-call-folio_test_lru-after-folio_.patch +io_uring-poll-fix-signed-comparison-in-io_poll_get_ownership.patch +net-tcp-md5-fix-mac-comparison-to-be-constant-time.patch +ksmbd-compare-macs-in-constant-time.patch +lib-crypto-mpi-fix-integer-underflow-in-mpi_read_raw_from_sgl.patch +f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch +f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch +smb-server-fix-active_num_conn-leak-on-transport-allocation-failure.patch +smb-server-fix-max_connections-off-by-one-in-tcp-accept-path.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 +ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch +net-packet-fix-toctou-race-on-mmap-d-vnet_hdr-in-tpacket_snd.patch +arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch +rtw88-8821ce-disable-pcie-aspm-l1-for-8821ce-using-chip-id.patch +wifi-rtw88-check-for-pci-upstream-bridge-existence.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 +nvme-fix-interpretation-of-dmrsl.patch +nvme-respect-nvme_quirk_disable_write_zeroes-when-wzsl-is-set.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-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.patch +net-qrtr-ns-limit-the-total-number-of-nodes.patch +net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch +fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch +randomize_kstack-maintain-kstack_offset-per-task.patch +mmc-sdhci-of-dwcmshc-disable-clock-before-dll-configuration.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 +crypto-nx-avoid-wflex-array-member-not-at-end-warning.patch +crypto-nx-migrate-to-scomp-api.patch +crypto-nx-fix-bounce-buffer-leaks-in-nx842_crypto_-alloc-free-_ctx.patch +erofs-fix-unsigned-underflow-in-z_erofs_lz4_handle_overlap.patch +ceph-only-d_add-negative-dentries-when-they-are-unhashed.patch +scsi-core-pm-rely-on-the-device-driver-core-for-async-power-management.patch +scsi-sd-add-error-handling-support-for-add_disk.patch +sd-rename-the-scsi_disk.dev-field.patch +scsi-sd-fix-missing-put_disk-when-device_add-disk_dev-fails.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 +tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch +rxrpc-fix-conn-level-packet-handling-to-unshare-response-packets.patch +mtd-docg3-fix-use-after-free-in-docg3_release.patch +mtd-docg3-convert-to-platform-remove-callback-returning-void.patch diff --git a/queue-5.15/smb-client-fix-oob-read-in-smb2_ioctl_query_info-query_info-path.patch b/queue-5.15/smb-client-fix-oob-read-in-smb2_ioctl_query_info-query_info-path.patch new file mode 100644 index 0000000000..85c7f8995c --- /dev/null +++ b/queue-5.15/smb-client-fix-oob-read-in-smb2_ioctl_query_info-query_info-path.patch @@ -0,0 +1,55 @@ +From stable+bounces-240989-greg=kroah.com@vger.kernel.org Fri Apr 24 20:38:37 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 11:08:27 -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: <20260424150827.2204387-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 +@@ -1826,6 +1826,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.15/smb-client-require-a-full-nfs-mode-sid-before-reading-mode-bits.patch b/queue-5.15/smb-client-require-a-full-nfs-mode-sid-before-reading-mode-bits.patch new file mode 100644 index 0000000000..3922b077ae --- /dev/null +++ b/queue-5.15/smb-client-require-a-full-nfs-mode-sid-before-reading-mode-bits.patch @@ -0,0 +1,46 @@ +From stable+bounces-240984-greg=kroah.com@vger.kernel.org Fri Apr 24 20:06:22 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:33:39 -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: <20260424143339.2171783-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 +@@ -807,6 +807,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.15/smb-server-fix-active_num_conn-leak-on-transport-allocation-failure.patch b/queue-5.15/smb-server-fix-active_num_conn-leak-on-transport-allocation-failure.patch new file mode 100644 index 0000000000..d0bbc18f53 --- /dev/null +++ b/queue-5.15/smb-server-fix-active_num_conn-leak-on-transport-allocation-failure.patch @@ -0,0 +1,72 @@ +From stable+bounces-240967-greg=kroah.com@vger.kernel.org Fri Apr 24 19:24:02 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 09:51:35 -0400 +Subject: smb: server: fix active_num_conn leak on transport allocation failure +To: stable@vger.kernel.org +Cc: Michael Bommarito , Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260424135135.2085884-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit 6551300dc452ac16a855a83dbd1e74899542d3b3 ] + +Commit 77ffbcac4e56 ("smb: server: fix leak of active_num_conn in +ksmbd_tcp_new_connection()") addressed the kthread_run() failure +path. The earlier alloc_transport() == NULL path in the same +function has the same leak, is reachable pre-authentication via any +TCP connect to port 445, and was empirically reproduced on UML +(ARCH=um, v7.0-rc7): a small number of forced allocation failures +were sufficient to put ksmbd into a state where every subsequent +connection attempt was rejected for the remainder of the boot. + +ksmbd_kthread_fn() increments active_num_conn before calling +ksmbd_tcp_new_connection() and discards the return value, so when +alloc_transport() returns NULL the socket is released and -ENOMEM +returned without decrementing the counter. Each such failure +permanently consumes one slot from the max_connections pool; once +cumulative failures reach the cap, atomic_inc_return() hits the +threshold on every subsequent accept and every new connection is +rejected. The counter is only reset by module reload. + +An unauthenticated remote attacker can drive the server toward the +memory pressure that makes alloc_transport() fail by holding open +connections with large RFC1002 lengths up to MAX_STREAM_PROT_LEN +(0x00FFFFFF); natural transient allocation failures on a loaded +host produce the same drift more slowly. + +Mirror the existing rollback pattern in ksmbd_kthread_fn(): on the +alloc_transport() failure path, decrement active_num_conn gated on +server_conf.max_connections. + +Repro details: with the patch reverted, forced alloc_transport() +NULL returns leaked counter slots and subsequent connection +attempts -- including legitimate connects issued after the +forced-fail window had closed -- were all rejected with "Limit the +maximum number of connections". With this patch applied, the same +connect sequence produces no rejections and the counter cycles +cleanly between zero and one on every accept. + +Fixes: 0d0d4680db22 ("ksmbd: add max connections parameter") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-6 +Assisted-by: Codex:gpt-5-4 +Signed-off-by: Michael Bommarito +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/transport_tcp.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/fs/ksmbd/transport_tcp.c ++++ b/fs/ksmbd/transport_tcp.c +@@ -191,6 +191,8 @@ static int ksmbd_tcp_new_connection(stru + t = alloc_transport(client_sk); + if (!t) { + sock_release(client_sk); ++ if (server_conf.max_connections) ++ atomic_dec(&active_num_conn); + return -ENOMEM; + } + diff --git a/queue-5.15/smb-server-fix-max_connections-off-by-one-in-tcp-accept-path.patch b/queue-5.15/smb-server-fix-max_connections-off-by-one-in-tcp-accept-path.patch new file mode 100644 index 0000000000..b07dc15a40 --- /dev/null +++ b/queue-5.15/smb-server-fix-max_connections-off-by-one-in-tcp-accept-path.patch @@ -0,0 +1,56 @@ +From stable+bounces-240975-greg=kroah.com@vger.kernel.org Fri Apr 24 19:40:06 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:08:09 -0400 +Subject: smb: server: fix max_connections off-by-one in tcp accept path +To: stable@vger.kernel.org +Cc: DaeMyung Kang , Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260424140809.2152073-1-sashal@kernel.org> + +From: DaeMyung Kang + +[ Upstream commit ce23158bfe584bd90d1918f279fdf9de57802012 ] + +The global max_connections check in ksmbd's TCP accept path counts +the newly accepted connection with atomic_inc_return(), but then +rejects the connection when the result is greater than or equal to +server_conf.max_connections. + +That makes the effective limit one smaller than configured. For +example: + +- max_connections=1 rejects the first connection +- max_connections=2 allows only one connection + +The per-IP limit in the same function uses <= correctly because it +counts only pre-existing connections. The global limit instead checks +the post-increment total, so it should reject only when that total +exceeds the configured maximum. + +Fix this by changing the comparison from >= to >, so exactly +max_connections simultaneous connections are allowed and the next one +is rejected. This matches the documented meaning of max_connections +in fs/smb/server/ksmbd_netlink.h as the "Number of maximum simultaneous +connections". + +Fixes: 0d0d4680db22 ("ksmbd: add max connections parameter") +Cc: stable@vger.kernel.org +Signed-off-by: DaeMyung Kang +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/ksmbd/transport_tcp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/ksmbd/transport_tcp.c ++++ b/fs/ksmbd/transport_tcp.c +@@ -248,7 +248,7 @@ static int ksmbd_kthread_fn(void *p) + } + + if (server_conf.max_connections && +- atomic_inc_return(&active_num_conn) >= server_conf.max_connections) { ++ atomic_inc_return(&active_num_conn) > server_conf.max_connections) { + pr_info_ratelimited("Limit the maximum number of connections(%u)\n", + atomic_read(&active_num_conn)); + atomic_dec(&active_num_conn); diff --git a/queue-5.15/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch b/queue-5.15/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch new file mode 100644 index 0000000000..13c4dce9ea --- /dev/null +++ b/queue-5.15/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch @@ -0,0 +1,69 @@ +From stable+bounces-242194-greg=kroah.com@vger.kernel.org Fri May 1 00:27:37 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 14:57:32 -0400 +Subject: thermal: core: Fix thermal zone governor cleanup issues +To: stable@vger.kernel.org +Cc: "Rafael J. Wysocki" , Sasha Levin +Message-ID: <20260430185732.1955098-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 +@@ -778,6 +778,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)) { +@@ -1260,8 +1261,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); +@@ -1385,8 +1388,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.15/tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch b/queue-5.15/tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch new file mode 100644 index 0000000000..60d3535d6b --- /dev/null +++ b/queue-5.15/tracepoint-balance-regfunc-on-func_add-failure-in-tracepoint_add_func.patch @@ -0,0 +1,56 @@ +From stable+bounces-245162-greg=kroah.com@vger.kernel.org Mon May 11 14:37:14 2026 +From: Sasha Levin +Date: Mon, 11 May 2026 04:57:26 -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: <20260511085726.1417414-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 +@@ -337,6 +337,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.15/udf-fix-partition-descriptor-append-bookkeeping.patch b/queue-5.15/udf-fix-partition-descriptor-append-bookkeeping.patch new file mode 100644 index 0000000000..88e2c057d3 --- /dev/null +++ b/queue-5.15/udf-fix-partition-descriptor-append-bookkeeping.patch @@ -0,0 +1,61 @@ +From stable+bounces-244849-greg=kroah.com@vger.kernel.org Sat May 9 05:30:16 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 19:59:53 -0400 +Subject: udf: fix partition descriptor append bookkeeping +To: stable@vger.kernel.org +Cc: Seohyeon Maeng , Jan Kara , Sasha Levin +Message-ID: <20260508235953.2245198-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 +@@ -1658,8 +1658,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); +@@ -1669,6 +1670,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.15/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch b/queue-5.15/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch new file mode 100644 index 0000000000..26ee96fc5c --- /dev/null +++ b/queue-5.15/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch @@ -0,0 +1,49 @@ +From stable+bounces-242195-greg=kroah.com@vger.kernel.org Fri May 1 00:28:01 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 14:57:55 -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: <20260430185755.1955735-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); diff --git a/queue-5.15/wifi-rtw88-check-for-pci-upstream-bridge-existence.patch b/queue-5.15/wifi-rtw88-check-for-pci-upstream-bridge-existence.patch new file mode 100644 index 0000000000..4c87ec4ec6 --- /dev/null +++ b/queue-5.15/wifi-rtw88-check-for-pci-upstream-bridge-existence.patch @@ -0,0 +1,48 @@ +From stable+bounces-242157-greg=kroah.com@vger.kernel.org Thu Apr 30 21:45:19 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 12:07:40 -0400 +Subject: wifi: rtw88: check for PCI upstream bridge existence +To: stable@vger.kernel.org +Cc: Fedor Pchelkin , Ping-Ke Shih , Sasha Levin +Message-ID: <20260430160740.1785374-2-sashal@kernel.org> + +From: Fedor Pchelkin + +[ Upstream commit eb101d2abdcccb514ca4fccd3b278dd8267374f6 ] + +pci_upstream_bridge() returns NULL if the device is on a root bus. If +8821CE is installed in the system with such a PCI topology, the probing +routine will crash. This has probably been unnoticed as 8821CE is mostly +supplied in laptops where there is a PCI-to-PCI bridge located upstream +from the device. However the card might be installed on a system with +different configuration. + +Check if the bridge does exist for the specific workaround to be applied. + +Found by Linux Verification Center (linuxtesting.org) with Svace static +analysis tool. + +Fixes: 24f5e38a13b5 ("rtw88: Disable PCIe ASPM while doing NAPI poll on 8821CE") +Cc: stable@vger.kernel.org +Signed-off-by: Fedor Pchelkin +Acked-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20260220094730.49791-1-pchelkin@ispras.ru +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/realtek/rtw88/pci.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/realtek/rtw88/pci.c ++++ b/drivers/net/wireless/realtek/rtw88/pci.c +@@ -1766,7 +1766,8 @@ int rtw_pci_probe(struct pci_dev *pdev, + } + + /* Disable PCIe ASPM L1 while doing NAPI poll for 8821CE */ +- if (rtwdev->chip->id == RTW_CHIP_TYPE_8821C && bridge->vendor == PCI_VENDOR_ID_INTEL) ++ if (rtwdev->chip->id == RTW_CHIP_TYPE_8821C && ++ bridge && bridge->vendor == PCI_VENDOR_ID_INTEL) + rtwpci->rx_no_aspm = true; + + rtw_pci_phy_cfg(rtwdev);