From: Greg Kroah-Hartman Date: Fri, 15 May 2026 10:27:34 +0000 (+0200) Subject: 6.6-stable patches X-Git-Tag: v5.10.256~5 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=12884f7cf623e33a673f3fe6d108178ce543a191;p=thirdparty%2Fkernel%2Fstable-queue.git 6.6-stable patches added patches: 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 arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch block-relax-pgmap-check-in-bio_add_page-for-compatible-zone-device-pages.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 firmware-google-framebuffer-do-not-unregister-platform-device.patch hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch ipmi-ssif-clean-up-kthread-on-errors.patch ipmi-ssif-fix-a-shutdown-race.patch ksmbd-replace-connection-list-with-hash-table.patch ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch ksmbd-reset-rcount-per-connection-in-ksmbd_conn_wait_idle_sess_id.patch ksmbd-use-msleep-instaed-of-schedule_timeout_interruptible.patch lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch media-rc-igorplugusb-heed-coherency-rules.patch media-rc-ttusbir-respect-dma-coherency-rules.patch mmc-core-optimize-time-for-secure-erase-trim-for-some-kingston-emmcs.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-qrtr-ns-limit-the-maximum-number-of-lookups.patch net-qrtr-ns-limit-the-maximum-server-registration-per-node.patch net-qrtr-ns-limit-the-total-number-of-nodes.patch pci-epf-mhi-return-0-not-remaining-timeout-when-edma-ops-complete.patch rdma-mana_ib-disable-rx-steering-on-rss-qp-destroy.patch sched-use-u64-for-bandwidth-ratio-calculations.patch smb-client-validate-the-whole-dacl-before-rewriting-it-in-cifsacl.patch smb-common-change-the-data-type-of-num_aces-to-le16.patch smb-move-some-duplicate-definitions-to-common-smbacl.h.patch spi-fix-resource-leaks-on-device-setup-failure.patch thermal-core-fix-thermal-zone-governor-cleanup-issues.patch udf-fix-partition-descriptor-append-bookkeeping.patch wifi-mt76-connac-introduce-helper-for-mt7925-chipset.patch wifi-mt76-mt792x-describe-usb-wfsys-reset-with-a-descriptor.patch wifi-mt76-mt792x-fix-mt7925u-usb-wfsys-reset-handling.patch wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch wifi-rtl8xxxu-fix-potential-use-of-uninitialized-value.patch xfs-fix-a-resource-leak-in-xfs_alloc_buftarg.patch --- diff --git a/queue-6.6/alsa-aoa-i2sbus-clear-stale-prepared-state.patch b/queue-6.6/alsa-aoa-i2sbus-clear-stale-prepared-state.patch new file mode 100644 index 0000000000..7a457a8a72 --- /dev/null +++ b/queue-6.6/alsa-aoa-i2sbus-clear-stale-prepared-state.patch @@ -0,0 +1,165 @@ +From stable+bounces-242459-greg=kroah.com@vger.kernel.org Fri May 1 17:54:30 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 11:54:21 -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: <20260501155421.3610801-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-6.6/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch b/queue-6.6/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch new file mode 100644 index 0000000000..c228215a71 --- /dev/null +++ b/queue-6.6/alsa-aoa-skip-devices-with-no-codecs-in-i2sbus_resume.patch @@ -0,0 +1,83 @@ +From stable+bounces-242478-greg=kroah.com@vger.kernel.org Fri May 1 19:27:59 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 13:27:47 -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: <20260501172747.3765281-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-6.6/alsa-aoa-use-guard-for-mutex-locks.patch b/queue-6.6/alsa-aoa-use-guard-for-mutex-locks.patch new file mode 100644 index 0000000000..45da687d22 --- /dev/null +++ b/queue-6.6/alsa-aoa-use-guard-for-mutex-locks.patch @@ -0,0 +1,1041 @@ +From stable+bounces-242458-greg=kroah.com@vger.kernel.org Fri May 1 17:56:08 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 11:54:20 -0400 +Subject: ALSA: aoa: Use guard() for mutex locks +To: stable@vger.kernel.org +Cc: Takashi Iwai , Sasha Levin +Message-ID: <20260501155421.3610801-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-6.6/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch b/queue-6.6/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch new file mode 100644 index 0000000000..f1607d17b7 --- /dev/null +++ b/queue-6.6/arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch @@ -0,0 +1,137 @@ +From stable+bounces-241691-greg=kroah.com@vger.kernel.org Tue Apr 28 17:25:10 2026 +From: Sasha Levin +Date: Tue, 28 Apr 2026 11:24:00 -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: <20260428152400.3033637-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 +@@ -870,10 +870,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); + } + +@@ -894,15 +898,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)); +@@ -927,15 +930,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)); +@@ -965,6 +965,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; + +@@ -986,6 +987,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-6.6/block-relax-pgmap-check-in-bio_add_page-for-compatible-zone-device-pages.patch b/queue-6.6/block-relax-pgmap-check-in-bio_add_page-for-compatible-zone-device-pages.patch new file mode 100644 index 0000000000..0023cb9dd5 --- /dev/null +++ b/queue-6.6/block-relax-pgmap-check-in-bio_add_page-for-compatible-zone-device-pages.patch @@ -0,0 +1,117 @@ +From stable+bounces-242626-greg=kroah.com@vger.kernel.org Sun May 3 01:58:16 2026 +From: Sasha Levin +Date: Sat, 2 May 2026 19:57:59 -0400 +Subject: block: relax pgmap check in bio_add_page for compatible zone device pages +To: stable@vger.kernel.org +Cc: Naman Jain , Christoph Hellwig , Jens Axboe , Sasha Levin +Message-ID: <20260502235759.921576-1-sashal@kernel.org> + +From: Naman Jain + +[ Upstream commit 41c665aae2b5dbecddddcc8ace344caf630cc7a4 ] + +bio_add_page() and bio_integrity_add_page() reject pages from different +dev_pagemaps entirely, returning 0 even when those pages have compatible +DMA mapping requirements. This forces callers to start a new bio when +buffers span pgmap boundaries, even though the pages could safely coexist +as separate bvec entries. + +This matters for guests where memory is registered through +devm_memremap_pages() with MEMORY_DEVICE_GENERIC in multiple calls, +creating separate dev_pagemaps for each chunk. When a direct I/O buffer +spans two such chunks, bio_add_page() rejects the second page, forcing an +unnecessary bio split or I/O failure. + +Introduce zone_device_pages_compatible() in blk.h to check whether two +pages can coexist in the same bio as separate bvec entries. The block DMA +iterator (blk_dma_map_iter_start) caches the P2PDMA mapping state from the +first segment and applies it to all others, so P2PDMA pages from different +pgmaps must not be mixed, and neither must P2PDMA and non-P2PDMA pages. +All other combinations (MEMORY_DEVICE_GENERIC pages from different pgmaps, +or MEMORY_DEVICE_GENERIC with normal RAM) use the same dma_map_phys path +and are safe. + +Replace the blanket zone_device_pages_have_same_pgmap() rejection with +zone_device_pages_compatible(), while keeping +zone_device_pages_have_same_pgmap() as a merge guard. +Pages from different pgmaps can be added as separate bvec entries but +must not be coalesced into the same segment, as that would make +it impossible to recover the correct pgmap via page_pgmap(). + +Fixes: 49580e690755 ("block: add check when merging zone device pages") +Cc: stable@vger.kernel.org +Signed-off-by: Naman Jain +Reviewed-by: Christoph Hellwig +Link: https://patch.msgid.link/20260410153414.4159050-3-namjain@linux.microsoft.com +Signed-off-by: Jens Axboe +[ restructured combined `if` into explicit `bv` block ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + block/bio-integrity.c | 2 ++ + block/bio.c | 14 +++++++++----- + block/blk.h | 19 +++++++++++++++++++ + 3 files changed, 30 insertions(+), 5 deletions(-) + +--- a/block/bio-integrity.c ++++ b/block/bio-integrity.c +@@ -134,6 +134,8 @@ int bio_integrity_add_page(struct bio *b + struct bio_vec *bv = &bip->bip_vec[bip->bip_vcnt - 1]; + bool same_page = false; + ++ if (!zone_device_pages_compatible(bv->bv_page, page)) ++ return 0; + if (bvec_try_merge_hw_page(q, bv, page, len, offset, + &same_page)) { + bip->bip_iter.bi_size += len; +--- a/block/bio.c ++++ b/block/bio.c +@@ -1098,11 +1098,15 @@ int bio_add_page(struct bio *bio, struct + if (bio->bi_iter.bi_size > UINT_MAX - len) + return 0; + +- if (bio->bi_vcnt > 0 && +- bvec_try_merge_page(&bio->bi_io_vec[bio->bi_vcnt - 1], +- page, len, offset, &same_page)) { +- bio->bi_iter.bi_size += len; +- return len; ++ if (bio->bi_vcnt > 0) { ++ struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; ++ ++ if (!zone_device_pages_compatible(bv->bv_page, page)) ++ return 0; ++ if (bvec_try_merge_page(bv, page, len, offset, &same_page)) { ++ bio->bi_iter.bi_size += len; ++ return len; ++ } + } + + if (bio->bi_vcnt >= bio->bi_max_vecs) +--- a/block/blk.h ++++ b/block/blk.h +@@ -104,6 +104,25 @@ static inline bool biovec_phys_mergeable + return true; + } + ++/* ++ * Check if two pages from potentially different zone device pgmaps can ++ * coexist as separate bvec entries in the same bio. ++ * ++ * The block DMA iterator (blk_dma_map_iter_start) caches the P2PDMA mapping ++ * state from the first segment and applies it to all subsequent segments, so ++ * P2PDMA pages from different pgmaps must not be mixed in the same bio. ++ * ++ * Other zone device types (FS_DAX, GENERIC) use the same dma_map_phys() path ++ * as normal RAM. PRIVATE and COHERENT pages never appear in bios. ++ */ ++static inline bool zone_device_pages_compatible(const struct page *a, ++ const struct page *b) ++{ ++ if (is_pci_p2pdma_page(a) || is_pci_p2pdma_page(b)) ++ return zone_device_pages_have_same_pgmap(a, b); ++ return true; ++} ++ + static inline bool __bvec_gap_to_prev(const struct queue_limits *lim, + struct bio_vec *bprv, unsigned int offset) + { diff --git a/queue-6.6/f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch b/queue-6.6/f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch new file mode 100644 index 0000000000..e89fd31b0f --- /dev/null +++ b/queue-6.6/f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch @@ -0,0 +1,158 @@ +From stable+bounces-241123-greg=kroah.com@vger.kernel.org Sat Apr 25 13:18:11 2026 +From: Sasha Levin +Date: Sat, 25 Apr 2026 07:18:05 -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: <20260425111805.3708792-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 +[ dereferenced flags pointer (`*flags & SB_RDONLY`) to match `int *flags` remount signature ] +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 | 11 ++++++++--- + 3 files changed, 12 insertions(+), 7 deletions(-) + +--- a/fs/f2fs/f2fs.h ++++ b/fs/f2fs/f2fs.h +@@ -3722,7 +3722,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 +@@ -1873,7 +1873,7 @@ void f2fs_stop_discard_thread(struct f2f + * + * Return true if issued all discard cmd or no discard cmd need issue, otherwise return false. + */ +-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; +@@ -1890,7 +1890,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; + } + +@@ -2349,7 +2349,7 @@ static void destroy_discard_cmd_control( + * Recovery can cache discard commands, so in error path of + * fill_super(), it needs to give a chance to handle them. + */ +- 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 +@@ -1612,7 +1612,7 @@ static void f2fs_put_super(struct super_ + } + + /* be sure to wait for any on-going discard commands */ +- done = f2fs_issue_discard_timeout(sbi); ++ done = f2fs_issue_discard_timeout(sbi, true); + if (f2fs_realtime_discard_enable(sbi) && !sbi->discard_blks && done) { + struct cp_control cpc = { + .reason = CP_UMOUNT | CP_TRIMMED, +@@ -1754,7 +1754,7 @@ static int f2fs_unfreeze(struct super_bl + * will recover after removal of snapshot. + */ + if (test_opt(sbi, DISCARD) && !f2fs_hw_support_discard(sbi)) +- f2fs_issue_discard_timeout(sbi); ++ f2fs_issue_discard_timeout(sbi, true); + + clear_sbi_flag(F2FS_SB(sb), SBI_IS_FREEZING); + return 0; +@@ -2515,7 +2515,12 @@ static int f2fs_remount(struct super_blo + need_stop_discard = true; + } else { + f2fs_stop_discard_thread(sbi); +- f2fs_issue_discard_timeout(sbi); ++ /* ++ * f2fs_ioc_fitrim() won't race w/ "remount ro" ++ * so it's safe to check discard_cmd_cnt in ++ * f2fs_issue_discard_timeout(). ++ */ ++ f2fs_issue_discard_timeout(sbi, *flags & SB_RDONLY); + need_restart_discard = true; + } + } diff --git a/queue-6.6/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch b/queue-6.6/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch new file mode 100644 index 0000000000..54a6c79310 --- /dev/null +++ b/queue-6.6/f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch @@ -0,0 +1,76 @@ +From stable+bounces-240662-greg=kroah.com@vger.kernel.org Fri Apr 24 15:29:00 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 09:28:50 -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: <20260424132850.1947809-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 +@@ -356,6 +356,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); + +@@ -367,8 +369,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-6.6/fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch b/queue-6.6/fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch new file mode 100644 index 0000000000..ebe828648d --- /dev/null +++ b/queue-6.6/fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch @@ -0,0 +1,370 @@ +From stable+bounces-243987-greg=kroah.com@vger.kernel.org Tue May 5 08:00:14 2026 +From: Sasha Levin +Date: Tue, 5 May 2026 02:00:00 -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: <20260505060001.225157-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`, and dropped `mutex_destroy(&fbdefio->lock)` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/video/fbdev/core/fb_defio.c | 179 ++++++++++++++++++++++++++++-------- + include/linux/fb.h | 4 + 2 files changed, 145 insertions(+), 38 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,15 @@ 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,15 +233,24 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); + * Adds a page to the dirty list. Call this from struct + * vm_operations_struct.page_mkwrite. + */ +-static vm_fault_t fb_deferred_io_track_page(struct fb_info *info, unsigned long offset, +- struct page *page) ++static vm_fault_t fb_deferred_io_track_page(struct fb_deferred_io_state *fbdefio_state, ++ unsigned long offset, struct page *page) + { +- struct fb_deferred_io *fbdefio = info->fbdefio; ++ struct fb_info *info; ++ struct fb_deferred_io *fbdefio; + struct fb_deferred_io_pageref *pageref; + vm_fault_t ret; + + /* 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; + + pageref = fb_deferred_io_pageref_get(info, offset, page); + if (WARN_ON_ONCE(!pageref)) { +@@ -169,50 +268,38 @@ static vm_fault_t fb_deferred_io_track_p + */ + 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; + } + +-/* +- * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O +- * @fb_info: The fbdev info structure +- * @vmf: The VM fault +- * +- * This is a callback we get when userspace first tries to +- * write to the page. We schedule a workqueue. That workqueue +- * will eventually mkclean the touched pages and execute the +- * deferred framebuffer IO. Then if userspace touches a page +- * again, we repeat the same scheme. +- * +- * Returns: +- * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise. +- */ +-static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf) ++static vm_fault_t fb_deferred_io_page_mkwrite(struct fb_deferred_io_state *fbdefio_state, ++ struct vm_fault *vmf) + { + unsigned long offset = vmf->pgoff << PAGE_SHIFT; + struct page *page = vmf->page; + + file_update_time(vmf->vma->vm_file); + +- return fb_deferred_io_track_page(info, offset, page); ++ return fb_deferred_io_track_page(fbdefio_state, offset, page); + } + +-/* vm_ops->page_mkwrite handler */ + static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf) + { +- struct fb_info *info = vmf->vma->vm_private_data; ++ struct fb_deferred_io_state *fbdefio_state = vmf->vma->vm_private_data; + +- return fb_deferred_io_page_mkwrite(info, vmf); ++ return fb_deferred_io_page_mkwrite(fbdefio_state, vmf); + } + + 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, + }; +@@ -227,7 +314,10 @@ int fb_deferred_io_mmap(struct fb_info * + vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP); + if (!(info->flags & FBINFO_VIRTFB)) + vm_flags_set(vma, 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; + } + EXPORT_SYMBOL_GPL(fb_deferred_io_mmap); +@@ -238,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); +@@ -255,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; +@@ -270,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 */ +@@ -289,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); +@@ -333,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 +@@ -214,11 +214,12 @@ 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 (*deferred_io)(struct fb_info *info, struct list_head *pagelist); + }; ++ ++struct fb_deferred_io_state; + #endif + + /* +@@ -476,6 +477,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-6.6/firmware-google-framebuffer-do-not-unregister-platform-device.patch b/queue-6.6/firmware-google-framebuffer-do-not-unregister-platform-device.patch new file mode 100644 index 0000000000..be9f94078f --- /dev/null +++ b/queue-6.6/firmware-google-framebuffer-do-not-unregister-platform-device.patch @@ -0,0 +1,68 @@ +From stable+bounces-244118-greg=kroah.com@vger.kernel.org Tue May 5 14:06:19 2026 +From: Sasha Levin +Date: Tue, 5 May 2026 08:01:31 -0400 +Subject: firmware: google: framebuffer: Do not unregister platform device +To: stable@vger.kernel.org +Cc: Thomas Zimmermann , Tzung-Bi Shih , Julius Werner , Javier Martinez Canillas , Hans de Goede , linux-fbdev@vger.kernel.org, Sasha Levin +Message-ID: <20260505120131.663403-1-sashal@kernel.org> + +From: Thomas Zimmermann + +[ Upstream commit 5cd28bd28c8ce426b56ce4230dbd17537181d5ad ] + +The native driver takes over the framebuffer aperture by removing the +system- framebuffer platform device. Afterwards the pointer in drvdata +is dangling. Remove the entire logic around drvdata and let the kernel's +aperture helpers handle this. The platform device depends on the native +hardware device instead of the coreboot device anyway. + +When commit 851b4c14532d ("firmware: coreboot: Add coreboot framebuffer +driver") added the coreboot framebuffer code, the kernel did not support +device-based aperture management. Instead native driviers only removed +the conflicting fbdev device. At that point, unregistering the framebuffer +device most likely worked correctly. It was definitely broken after +commit d9702b2a2171 ("fbdev/simplefb: Do not use struct +fb_info.apertures"). So take this commit for the Fixes tag. Earlier +releases might work depending on the native hardware driver. + +Signed-off-by: Thomas Zimmermann +Fixes: d9702b2a2171 ("fbdev/simplefb: Do not use struct fb_info.apertures") +Acked-by: Tzung-Bi Shih +Acked-by: Julius Werner +Cc: Thomas Zimmermann +Cc: Javier Martinez Canillas +Cc: Hans de Goede +Cc: linux-fbdev@vger.kernel.org +Cc: # v6.3+ +Link: https://patch.msgid.link/20260217155836.96267-2-tzimmermann@suse.de +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/firmware/google/framebuffer-coreboot.c | 10 ---------- + 1 file changed, 10 deletions(-) + +--- a/drivers/firmware/google/framebuffer-coreboot.c ++++ b/drivers/firmware/google/framebuffer-coreboot.c +@@ -64,22 +64,12 @@ static int framebuffer_probe(struct core + sizeof(pdata)); + if (IS_ERR(pdev)) + pr_warn("coreboot: could not register framebuffer\n"); +- else +- dev_set_drvdata(&dev->dev, pdev); + + return PTR_ERR_OR_ZERO(pdev); + } + +-static void framebuffer_remove(struct coreboot_device *dev) +-{ +- struct platform_device *pdev = dev_get_drvdata(&dev->dev); +- +- platform_device_unregister(pdev); +-} +- + static struct coreboot_driver framebuffer_driver = { + .probe = framebuffer_probe, +- .remove = framebuffer_remove, + .drv = { + .name = "framebuffer", + }, diff --git a/queue-6.6/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch b/queue-6.6/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch new file mode 100644 index 0000000000..b9f776ba53 --- /dev/null +++ b/queue-6.6/hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch @@ -0,0 +1,142 @@ +From stable+bounces-244863-greg=kroah.com@vger.kernel.org Sat May 9 02:49:09 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 20:48:54 -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: <20260509004855.2446851-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-6.6/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch b/queue-6.6/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch new file mode 100644 index 0000000000..31edbe3dd4 --- /dev/null +++ b/queue-6.6/hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch @@ -0,0 +1,189 @@ +From stable+bounces-244862-greg=kroah.com@vger.kernel.org Sat May 9 02:49:03 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 20:48:53 -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: <20260509004855.2446851-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 +@@ -535,6 +535,15 @@ int hfsplus_submit_bio(struct super_bloc + void **data, blk_opf_t opf); + 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-6.6/ipmi-ssif-clean-up-kthread-on-errors.patch b/queue-6.6/ipmi-ssif-clean-up-kthread-on-errors.patch new file mode 100644 index 0000000000..5ea837b7d8 --- /dev/null +++ b/queue-6.6/ipmi-ssif-clean-up-kthread-on-errors.patch @@ -0,0 +1,58 @@ +From stable+bounces-242438-greg=kroah.com@vger.kernel.org Fri May 1 16:07:17 2026 +From: Corey Minyard +Date: Fri, 1 May 2026 09:06:58 -0500 +Subject: ipmi:ssif: Clean up kthread on errors +To: stable@vger.kernel.org +Cc: Corey Minyard , Li Xiao <252270051@hdu.edu.cn> +Message-ID: <20260501140658.707484-2-corey@minyard.net> + +From: Corey Minyard + +If an error occurs after the ssif kthread is created, but before the +main IPMI code starts the ssif interface, the ssif kthread will not +be stopped. + +So make sure the kthread is stopped on an error condition if it is +running. + +Fixes: 259307074bfc ("ipmi: Add SMBus interface driver (SSIF)") +Reported-by: Li Xiao <<252270051@hdu.edu.cn> +Cc: stable@vger.kernel.org +Reviewed-by: Li Xiao <252270051@hdu.edu.cn> +Signed-off-by: Corey Minyard +(cherry picked from commit 75c486cb1bcaa1a3ec3a6438498176a3a4998ae4) +Signed-off-by: Greg Kroah-Hartman +--- + drivers/char/ipmi/ipmi_ssif.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +--- a/drivers/char/ipmi/ipmi_ssif.c ++++ b/drivers/char/ipmi/ipmi_ssif.c +@@ -1287,8 +1287,10 @@ static void shutdown_ssif(void *send_inf + ssif_info->stopping = true; + del_timer_sync(&ssif_info->watch_timer); + del_timer_sync(&ssif_info->retry_timer); +- if (ssif_info->thread) ++ if (ssif_info->thread) { + kthread_stop(ssif_info->thread); ++ ssif_info->thread = NULL; ++ } + } + + static void ssif_remove(struct i2c_client *client) +@@ -1913,6 +1915,15 @@ static int ssif_probe(struct i2c_client + + out: + if (rv) { ++ /* ++ * If ipmi_register_smi() starts the interface, it will ++ * call shutdown and that will free the thread and set ++ * it to NULL. Otherwise it must be freed here. ++ */ ++ if (ssif_info->thread) { ++ kthread_stop(ssif_info->thread); ++ ssif_info->thread = NULL; ++ } + if (addr_info) + addr_info->client = NULL; + diff --git a/queue-6.6/ipmi-ssif-fix-a-shutdown-race.patch b/queue-6.6/ipmi-ssif-fix-a-shutdown-race.patch new file mode 100644 index 0000000000..f11df614e5 --- /dev/null +++ b/queue-6.6/ipmi-ssif-fix-a-shutdown-race.patch @@ -0,0 +1,48 @@ +From stable+bounces-242437-greg=kroah.com@vger.kernel.org Fri May 1 16:08:23 2026 +From: Corey Minyard +Date: Fri, 1 May 2026 09:06:57 -0500 +Subject: ipmi:ssif: Fix a shutdown race +To: stable@vger.kernel.org +Cc: Corey Minyard , Corey Minyard +Message-ID: <20260501140658.707484-1-corey@minyard.net> + +From: Corey Minyard + +It was possible for the SSIF thread to stop and quit before the +kthread_stop() call because ssif->stopping was set before the +stop. So only exit the SSIF thread is kthread_should_stop() +returns true. + +There is no need to wake the thread, as the wait will be interrupted +by kthread_stop(). + +Signed-off-by: Corey Minyard +(cherry picked from commit 6bd0eb6d759b9a22c5509ea04e19c2e8407ba418) +Signed-off-by: Greg Kroah-Hartman +--- + drivers/char/ipmi/ipmi_ssif.c | 6 +----- + 1 file changed, 1 insertion(+), 5 deletions(-) + +--- a/drivers/char/ipmi/ipmi_ssif.c ++++ b/drivers/char/ipmi/ipmi_ssif.c +@@ -490,8 +490,6 @@ static int ipmi_ssif_thread(void *data) + /* Wait for something to do */ + result = wait_for_completion_interruptible( + &ssif_info->wake_thread); +- if (ssif_info->stopping) +- break; + if (result == -ERESTARTSYS) + continue; + init_completion(&ssif_info->wake_thread); +@@ -1289,10 +1287,8 @@ static void shutdown_ssif(void *send_inf + ssif_info->stopping = true; + del_timer_sync(&ssif_info->watch_timer); + del_timer_sync(&ssif_info->retry_timer); +- if (ssif_info->thread) { +- complete(&ssif_info->wake_thread); ++ if (ssif_info->thread) + kthread_stop(ssif_info->thread); +- } + } + + static void ssif_remove(struct i2c_client *client) diff --git a/queue-6.6/ksmbd-replace-connection-list-with-hash-table.patch b/queue-6.6/ksmbd-replace-connection-list-with-hash-table.patch new file mode 100644 index 0000000000..5d3521a540 --- /dev/null +++ b/queue-6.6/ksmbd-replace-connection-list-with-hash-table.patch @@ -0,0 +1,243 @@ +From stable+bounces-241112-greg=kroah.com@vger.kernel.org Sat Apr 25 11:11:42 2026 +From: Sasha Levin +Date: Sat, 25 Apr 2026 05:11:29 -0400 +Subject: ksmbd: replace connection list with hash table +To: stable@vger.kernel.org +Cc: Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260425091130.3330505-2-sashal@kernel.org> + +From: Namjae Jeon + +[ Upstream commit 0bcc831be535269556f59cb70396f7e34f03a276 ] + +Replace connection list with hash table to improve lookup performance. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: def036ef87f8 ("ksmbd: reset rcount per connection in ksmbd_conn_wait_idle_sess_id()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/server/connection.c | 23 +++++++++++------------ + fs/smb/server/connection.h | 6 ++++-- + fs/smb/server/smb2pdu.c | 4 ++-- + fs/smb/server/transport_rdma.c | 5 +++++ + fs/smb/server/transport_tcp.c | 25 +++++++++++++++++++++---- + 5 files changed, 43 insertions(+), 20 deletions(-) + +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -19,7 +19,7 @@ static DEFINE_MUTEX(init_lock); + + static struct ksmbd_conn_ops default_conn_ops; + +-LIST_HEAD(conn_list); ++DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS); + DECLARE_RWSEM(conn_list_lock); + + /** +@@ -33,7 +33,7 @@ DECLARE_RWSEM(conn_list_lock); + void ksmbd_conn_free(struct ksmbd_conn *conn) + { + down_write(&conn_list_lock); +- list_del(&conn->conns_list); ++ hash_del(&conn->hlist); + up_write(&conn_list_lock); + + xa_destroy(&conn->sessions); +@@ -78,7 +78,6 @@ struct ksmbd_conn *ksmbd_conn_alloc(void + + init_waitqueue_head(&conn->req_running_q); + init_waitqueue_head(&conn->r_count_q); +- INIT_LIST_HEAD(&conn->conns_list); + INIT_LIST_HEAD(&conn->requests); + INIT_LIST_HEAD(&conn->async_requests); + spin_lock_init(&conn->request_lock); +@@ -91,19 +90,17 @@ struct ksmbd_conn *ksmbd_conn_alloc(void + + init_rwsem(&conn->session_lock); + +- down_write(&conn_list_lock); +- list_add(&conn->conns_list, &conn_list); +- up_write(&conn_list_lock); + return conn; + } + + bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c) + { + struct ksmbd_conn *t; ++ int bkt; + bool ret = false; + + down_read(&conn_list_lock); +- list_for_each_entry(t, &conn_list, conns_list) { ++ hash_for_each(conn_list, bkt, t, hlist) { + if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE)) + continue; + +@@ -164,9 +161,10 @@ void ksmbd_conn_unlock(struct ksmbd_conn + void ksmbd_all_conn_set_status(u64 sess_id, u32 status) + { + struct ksmbd_conn *conn; ++ int bkt; + + down_read(&conn_list_lock); +- list_for_each_entry(conn, &conn_list, conns_list) { ++ hash_for_each(conn_list, bkt, conn, hlist) { + if (conn->binding || xa_load(&conn->sessions, sess_id)) + WRITE_ONCE(conn->status, status); + } +@@ -182,14 +180,14 @@ int ksmbd_conn_wait_idle_sess_id(struct + { + struct ksmbd_conn *conn; + int rc, retry_count = 0, max_timeout = 120; +- int rcount = 1; ++ int rcount = 1, bkt; + + retry_idle: + if (retry_count >= max_timeout) + return -EIO; + + down_read(&conn_list_lock); +- list_for_each_entry(conn, &conn_list, conns_list) { ++ hash_for_each(conn_list, bkt, conn, hlist) { + if (conn->binding || xa_load(&conn->sessions, sess_id)) { + if (conn == curr_conn) + rcount = 2; +@@ -480,10 +478,11 @@ static void stop_sessions(void) + { + struct ksmbd_conn *conn; + struct ksmbd_transport *t; ++ int bkt; + + again: + down_read(&conn_list_lock); +- list_for_each_entry(conn, &conn_list, conns_list) { ++ hash_for_each(conn_list, bkt, conn, hlist) { + t = conn->transport; + ksmbd_conn_set_exiting(conn); + if (t->ops->shutdown) { +@@ -494,7 +493,7 @@ again: + } + up_read(&conn_list_lock); + +- if (!list_empty(&conn_list)) { ++ if (!hash_empty(conn_list)) { + msleep(100); + goto again; + } +--- a/fs/smb/server/connection.h ++++ b/fs/smb/server/connection.h +@@ -52,11 +52,12 @@ struct ksmbd_conn { + u8 inet6_addr[16]; + #endif + }; ++ unsigned int inet_hash; + char *request_buf; + struct ksmbd_transport *transport; + struct nls_table *local_nls; + struct unicode_map *um; +- struct list_head conns_list; ++ struct hlist_node hlist; + struct rw_semaphore session_lock; + /* smb session 1 per user */ + struct xarray sessions; +@@ -151,7 +152,8 @@ struct ksmbd_transport { + #define KSMBD_TCP_SEND_TIMEOUT (5 * HZ) + #define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr)) + +-extern struct list_head conn_list; ++#define CONN_HASH_BITS 12 ++extern DECLARE_HASHTABLE(conn_list, CONN_HASH_BITS); + extern struct rw_semaphore conn_list_lock; + + bool ksmbd_conn_alive(struct ksmbd_conn *conn); +--- a/fs/smb/server/smb2pdu.c ++++ b/fs/smb/server/smb2pdu.c +@@ -7348,7 +7348,7 @@ int smb2_lock(struct ksmbd_work *work) + int nolock = 0; + LIST_HEAD(lock_list); + LIST_HEAD(rollback_list); +- int prior_lock = 0; ++ int prior_lock = 0, bkt; + + WORK_BUFFERS(work, req, rsp); + +@@ -7458,7 +7458,7 @@ int smb2_lock(struct ksmbd_work *work) + nolock = 1; + /* check locks in connection list */ + down_read(&conn_list_lock); +- list_for_each_entry(conn, &conn_list, conns_list) { ++ hash_for_each(conn_list, bkt, conn, hlist) { + spin_lock(&conn->llist_lock); + list_for_each_entry_safe(cmp_lock, tmp2, &conn->lock_list, clist) { + if (file_inode(cmp_lock->fl->fl_file) != +--- a/fs/smb/server/transport_rdma.c ++++ b/fs/smb/server/transport_rdma.c +@@ -381,6 +381,11 @@ static struct smb_direct_transport *allo + conn = ksmbd_conn_alloc(); + if (!conn) + goto err; ++ ++ down_write(&conn_list_lock); ++ hash_add(conn_list, &conn->hlist, 0); ++ up_write(&conn_list_lock); ++ + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_smb_direct_transport_ops; +--- a/fs/smb/server/transport_tcp.c ++++ b/fs/smb/server/transport_tcp.c +@@ -89,13 +89,21 @@ static struct tcp_transport *alloc_trans + } + + #if IS_ENABLED(CONFIG_IPV6) +- if (client_sk->sk->sk_family == AF_INET6) ++ if (client_sk->sk->sk_family == AF_INET6) { + memcpy(&conn->inet6_addr, &client_sk->sk->sk_v6_daddr, 16); +- else ++ conn->inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr); ++ } else { + conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr; ++ conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); ++ } + #else + conn->inet_addr = inet_sk(client_sk->sk)->inet_daddr; ++ conn->inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); + #endif ++ down_write(&conn_list_lock); ++ hash_add(conn_list, &conn->hlist, conn->inet_hash); ++ up_write(&conn_list_lock); ++ + conn->transport = KSMBD_TRANS(t); + KSMBD_TRANS(t)->conn = conn; + KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops; +@@ -242,7 +250,7 @@ static int ksmbd_kthread_fn(void *p) + struct socket *client_sk = NULL; + struct interface *iface = (struct interface *)p; + struct ksmbd_conn *conn; +- int ret; ++ int ret, inet_hash; + unsigned int max_ip_conns; + + while (!kthread_should_stop()) { +@@ -267,9 +275,18 @@ static int ksmbd_kthread_fn(void *p) + /* + * Limits repeated connections from clients with the same IP. + */ ++#if IS_ENABLED(CONFIG_IPV6) ++ if (client_sk->sk->sk_family == AF_INET6) ++ inet_hash = ipv6_addr_hash(&client_sk->sk->sk_v6_daddr); ++ else ++ inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); ++#else ++ inet_hash = ipv4_addr_hash(inet_sk(client_sk->sk)->inet_daddr); ++#endif ++ + max_ip_conns = 0; + down_read(&conn_list_lock); +- list_for_each_entry(conn, &conn_list, conns_list) { ++ hash_for_each_possible(conn_list, conn, hlist, inet_hash) { + #if IS_ENABLED(CONFIG_IPV6) + if (client_sk->sk->sk_family == AF_INET6) { + if (memcmp(&client_sk->sk->sk_v6_daddr, diff --git a/queue-6.6/ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch b/queue-6.6/ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch new file mode 100644 index 0000000000..192d77cb44 --- /dev/null +++ b/queue-6.6/ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch @@ -0,0 +1,106 @@ +From stable+bounces-240658-greg=kroah.com@vger.kernel.org Fri Apr 24 15:05:17 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 09:04:01 -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: <20260424130401.1917926-3-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 +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/server/smbacl.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +--- a/fs/smb/server/smbacl.c ++++ b/fs/smb/server/smbacl.c +@@ -1298,10 +1298,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 < le16_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); +@@ -1319,13 +1322,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 < le16_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-6.6/ksmbd-reset-rcount-per-connection-in-ksmbd_conn_wait_idle_sess_id.patch b/queue-6.6/ksmbd-reset-rcount-per-connection-in-ksmbd_conn_wait_idle_sess_id.patch new file mode 100644 index 0000000000..3c8bff7d33 --- /dev/null +++ b/queue-6.6/ksmbd-reset-rcount-per-connection-in-ksmbd_conn_wait_idle_sess_id.patch @@ -0,0 +1,70 @@ +From stable+bounces-241113-greg=kroah.com@vger.kernel.org Sat Apr 25 11:11:38 2026 +From: Sasha Levin +Date: Sat, 25 Apr 2026 05:11:30 -0400 +Subject: ksmbd: reset rcount per connection in ksmbd_conn_wait_idle_sess_id() +To: stable@vger.kernel.org +Cc: DaeMyung Kang , Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260425091130.3330505-3-sashal@kernel.org> + +From: DaeMyung Kang + +[ Upstream commit def036ef87f8641c1c525d5ae17438d7a1006491 ] + +rcount is intended to be connection-specific: 2 for curr_conn, 1 for +every other connection sharing the same session. However, it is +initialised only once before the hash iteration and is never reset. +After the loop visits curr_conn, later sibling connections are also +checked against rcount == 2, so a sibling with req_running == 1 is +incorrectly treated as idle. This makes the outcome depend on the +hash iteration order: whether a given sibling is checked against the +loose (< 2) or the strict (< 1) threshold is decided by whether it +happens to be visited before or after curr_conn. + +The function's contract is "wait until every connection sharing this +session is idle" so that destroy_previous_session() can safely tear +the session down. The latched rcount violates that contract and +reopens the teardown race window the wait logic was meant to close: +destroy_previous_session() may proceed before sibling channels have +actually quiesced, overlapping session teardown with in-flight work +on those connections. + +Recompute rcount inside the loop so each connection is compared +against its own threshold regardless of iteration order. + +This is a code-inspection fix for an iteration-order-dependent logic +error; a targeted reproducer would require SMB3 multichannel with +in-flight work on a sibling channel landing after curr_conn in hash +order, which is not something that can be triggered reliably. + +Fixes: 76e98a158b20 ("ksmbd: fix race condition between destroy_previous_session() and smb2 operations()") +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/smb/server/connection.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -180,7 +180,7 @@ int ksmbd_conn_wait_idle_sess_id(struct + { + struct ksmbd_conn *conn; + int rc, retry_count = 0, max_timeout = 120; +- int rcount = 1, bkt; ++ int rcount, bkt; + + retry_idle: + if (retry_count >= max_timeout) +@@ -189,8 +189,7 @@ retry_idle: + down_read(&conn_list_lock); + hash_for_each(conn_list, bkt, conn, hlist) { + if (conn->binding || xa_load(&conn->sessions, sess_id)) { +- if (conn == curr_conn) +- rcount = 2; ++ rcount = (conn == curr_conn) ? 2 : 1; + if (atomic_read(&conn->req_running) >= rcount) { + rc = wait_event_timeout(conn->req_running_q, + atomic_read(&conn->req_running) < rcount, diff --git a/queue-6.6/ksmbd-use-msleep-instaed-of-schedule_timeout_interruptible.patch b/queue-6.6/ksmbd-use-msleep-instaed-of-schedule_timeout_interruptible.patch new file mode 100644 index 0000000000..51b97307be --- /dev/null +++ b/queue-6.6/ksmbd-use-msleep-instaed-of-schedule_timeout_interruptible.patch @@ -0,0 +1,35 @@ +From stable+bounces-241111-greg=kroah.com@vger.kernel.org Sat Apr 25 11:11:38 2026 +From: Sasha Levin +Date: Sat, 25 Apr 2026 05:11:28 -0400 +Subject: ksmbd: use msleep instaed of schedule_timeout_interruptible() +To: stable@vger.kernel.org +Cc: Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260425091130.3330505-1-sashal@kernel.org> + +From: Namjae Jeon + +[ Upstream commit f75f8bdd4ff4830abe31a1b94892eb12b85b9535 ] + +use msleep instaed of schedule_timeout_interruptible() +to guarantee the task delays as expected. + +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: def036ef87f8 ("ksmbd: reset rcount per connection in ksmbd_conn_wait_idle_sess_id()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/server/connection.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/smb/server/connection.c ++++ b/fs/smb/server/connection.c +@@ -495,7 +495,7 @@ again: + up_read(&conn_list_lock); + + if (!list_empty(&conn_list)) { +- schedule_timeout_interruptible(HZ / 10); /* 100ms */ ++ msleep(100); + goto again; + } + } diff --git a/queue-6.6/lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch b/queue-6.6/lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch new file mode 100644 index 0000000000..3adc49e6d5 --- /dev/null +++ b/queue-6.6/lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch @@ -0,0 +1,170 @@ +From stable+bounces-241755-greg=kroah.com@vger.kernel.org Tue Apr 28 20:20:17 2026 +From: Sasha Levin +Date: Tue, 28 Apr 2026 14:19:44 -0400 +Subject: lib: test_hmm: evict device pages on file close to avoid use-after-free +To: stable@vger.kernel.org +Cc: Alistair Popple , Zenghui Yu , Balbir Singh , David Hildenbrand , Jason Gunthorpe , Leon Romanovsky , Liam Howlett , "Lorenzo Stoakes (Oracle)" , Michal Hocko , Mike Rapoport , Suren Baghdasaryan , Matthew Brost , Andrew Morton , Sasha Levin +Message-ID: <20260428181944.3126951-1-sashal@kernel.org> + +From: Alistair Popple + +[ Upstream commit 744dd97752ef1076a8d8672bb0d8aa2c7abc1144 ] + +Patch series "Minor hmm_test fixes and cleanups". + +Two bugfixes a cleanup for the HMM kernel selftests. These were mostly +reported by Zenghui Yu with special thanks to Lorenzo for analysing and +pointing out the problems. + +This patch (of 3): + +When dmirror_fops_release() is called it frees the dmirror struct but +doesn't migrate device private pages back to system memory first. This +leaves those pages with a dangling zone_device_data pointer to the freed +dmirror. + +If a subsequent fault occurs on those pages (eg. during coredump) the +dmirror_devmem_fault() callback dereferences the stale pointer causing a +kernel panic. This was reported [1] when running mm/ksft_hmm.sh on arm64, +where a test failure triggered SIGABRT and the resulting coredump walked +the VMAs faulting in the stale device private pages. + +Fix this by calling dmirror_device_evict_chunk() for each devmem chunk in +dmirror_fops_release() to migrate all device private pages back to system +memory before freeing the dmirror struct. The function is moved earlier +in the file to avoid a forward declaration. + +Link: https://lore.kernel.org/20260331063445.3551404-1-apopple@nvidia.com +Link: https://lore.kernel.org/20260331063445.3551404-2-apopple@nvidia.com +Fixes: b2ef9f5a5cb3 ("mm/hmm/test: add selftest driver for HMM") +Signed-off-by: Alistair Popple +Reported-by: Zenghui Yu +Closes: https://lore.kernel.org/linux-mm/8bd0396a-8997-4d2e-a13f-5aac033083d7@linux.dev/ +Reviewed-by: Balbir Singh +Tested-by: Zenghui Yu +Cc: David Hildenbrand +Cc: Jason Gunthorpe +Cc: Leon Romanovsky +Cc: Liam Howlett +Cc: Lorenzo Stoakes (Oracle) +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Suren Baghdasaryan +Cc: Zenghui Yu +Cc: Matthew Brost +Cc: +Signed-off-by: Andrew Morton +[ kept the existing simpler `dmirror_device_evict_chunk()` body instead of the upstream compound-folio version ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + lib/test_hmm.c | 86 ++++++++++++++++++++++++++++++++------------------------- + 1 file changed, 49 insertions(+), 37 deletions(-) + +--- a/lib/test_hmm.c ++++ b/lib/test_hmm.c +@@ -183,11 +183,60 @@ static int dmirror_fops_open(struct inod + return 0; + } + ++static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk) ++{ ++ unsigned long start_pfn = chunk->pagemap.range.start >> PAGE_SHIFT; ++ unsigned long end_pfn = chunk->pagemap.range.end >> PAGE_SHIFT; ++ unsigned long npages = end_pfn - start_pfn + 1; ++ unsigned long i; ++ unsigned long *src_pfns; ++ unsigned long *dst_pfns; ++ ++ src_pfns = kvcalloc(npages, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); ++ dst_pfns = kvcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); ++ ++ migrate_device_range(src_pfns, start_pfn, npages); ++ for (i = 0; i < npages; i++) { ++ struct page *dpage, *spage; ++ ++ spage = migrate_pfn_to_page(src_pfns[i]); ++ if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE)) ++ continue; ++ ++ if (WARN_ON(!is_device_private_page(spage) && ++ !is_device_coherent_page(spage))) ++ continue; ++ spage = BACKING_PAGE(spage); ++ dpage = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NOFAIL); ++ lock_page(dpage); ++ copy_highpage(dpage, spage); ++ dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)); ++ if (src_pfns[i] & MIGRATE_PFN_WRITE) ++ dst_pfns[i] |= MIGRATE_PFN_WRITE; ++ } ++ migrate_device_pages(src_pfns, dst_pfns, npages); ++ migrate_device_finalize(src_pfns, dst_pfns, npages); ++ kvfree(src_pfns); ++ kvfree(dst_pfns); ++} ++ + static int dmirror_fops_release(struct inode *inode, struct file *filp) + { + struct dmirror *dmirror = filp->private_data; ++ struct dmirror_device *mdevice = dmirror->mdevice; ++ int i; + + mmu_interval_notifier_remove(&dmirror->notifier); ++ ++ if (mdevice->devmem_chunks) { ++ for (i = 0; i < mdevice->devmem_count; i++) { ++ struct dmirror_chunk *devmem = ++ mdevice->devmem_chunks[i]; ++ ++ dmirror_device_evict_chunk(devmem); ++ } ++ } ++ + xa_destroy(&dmirror->pt); + kfree(dmirror); + return 0; +@@ -1217,43 +1266,6 @@ static int dmirror_snapshot(struct dmirr + return ret; + } + +-static void dmirror_device_evict_chunk(struct dmirror_chunk *chunk) +-{ +- unsigned long start_pfn = chunk->pagemap.range.start >> PAGE_SHIFT; +- unsigned long end_pfn = chunk->pagemap.range.end >> PAGE_SHIFT; +- unsigned long npages = end_pfn - start_pfn + 1; +- unsigned long i; +- unsigned long *src_pfns; +- unsigned long *dst_pfns; +- +- src_pfns = kvcalloc(npages, sizeof(*src_pfns), GFP_KERNEL | __GFP_NOFAIL); +- dst_pfns = kvcalloc(npages, sizeof(*dst_pfns), GFP_KERNEL | __GFP_NOFAIL); +- +- migrate_device_range(src_pfns, start_pfn, npages); +- for (i = 0; i < npages; i++) { +- struct page *dpage, *spage; +- +- spage = migrate_pfn_to_page(src_pfns[i]); +- if (!spage || !(src_pfns[i] & MIGRATE_PFN_MIGRATE)) +- continue; +- +- if (WARN_ON(!is_device_private_page(spage) && +- !is_device_coherent_page(spage))) +- continue; +- spage = BACKING_PAGE(spage); +- dpage = alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NOFAIL); +- lock_page(dpage); +- copy_highpage(dpage, spage); +- dst_pfns[i] = migrate_pfn(page_to_pfn(dpage)); +- if (src_pfns[i] & MIGRATE_PFN_WRITE) +- dst_pfns[i] |= MIGRATE_PFN_WRITE; +- } +- migrate_device_pages(src_pfns, dst_pfns, npages); +- migrate_device_finalize(src_pfns, dst_pfns, npages); +- kvfree(src_pfns); +- kvfree(dst_pfns); +-} +- + /* Removes free pages from the free list so they can't be re-allocated */ + static void dmirror_remove_free_pages(struct dmirror_chunk *devmem) + { diff --git a/queue-6.6/media-rc-igorplugusb-heed-coherency-rules.patch b/queue-6.6/media-rc-igorplugusb-heed-coherency-rules.patch new file mode 100644 index 0000000000..19c1c412f4 --- /dev/null +++ b/queue-6.6/media-rc-igorplugusb-heed-coherency-rules.patch @@ -0,0 +1,85 @@ +From stable+bounces-242523-greg=kroah.com@vger.kernel.org Fri May 1 23:07:36 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 17:06: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: <20260501210621.4038633-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(*ir->request, GFP_KERNEL) with kzalloc(sizeof(*ir->request), GFP_KERNEL) ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/rc/igorplugusb.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +--- a/drivers/media/rc/igorplugusb.c ++++ b/drivers/media/rc/igorplugusb.c +@@ -34,7 +34,7 @@ struct igorplugusb { + struct device *dev; + + struct urb *urb; +- struct usb_ctrlrequest request; ++ struct usb_ctrlrequest *request; + + struct timer_list timer; + +@@ -122,7 +122,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 && ret != -EPERM) +@@ -164,13 +164,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(MAX_PACKET); ++ ir->request->bRequest = GET_INFRACODE; ++ ir->request->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN; ++ ir->request->wLength = cpu_to_le16(MAX_PACKET); + + ir->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!ir->urb) +@@ -228,6 +232,7 @@ fail: + usb_free_urb(ir->urb); + rc_free_device(ir->rc); + kfree(ir->buf_in); ++ kfree(ir->request); + + return ret; + } +@@ -243,6 +248,7 @@ static void igorplugusb_disconnect(struc + usb_unpoison_urb(ir->urb); + usb_free_urb(ir->urb); + kfree(ir->buf_in); ++ kfree(ir->request); + } + + static const struct usb_device_id igorplugusb_table[] = { diff --git a/queue-6.6/media-rc-ttusbir-respect-dma-coherency-rules.patch b/queue-6.6/media-rc-ttusbir-respect-dma-coherency-rules.patch new file mode 100644 index 0000000000..fde864d348 --- /dev/null +++ b/queue-6.6/media-rc-ttusbir-respect-dma-coherency-rules.patch @@ -0,0 +1,83 @@ +From stable+bounces-242470-greg=kroah.com@vger.kernel.org Fri May 1 18:59:15 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 12:59:04 -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: <20260501165904.3629254-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; +@@ -186,13 +186,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++) { +@@ -281,8 +284,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"; +@@ -351,6 +354,7 @@ out: + kfree(tt); + } + rc_free_device(rc); ++ kfree(buffer); + + return ret; + } +@@ -373,6 +377,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-6.6/mmc-core-optimize-time-for-secure-erase-trim-for-some-kingston-emmcs.patch b/queue-6.6/mmc-core-optimize-time-for-secure-erase-trim-for-some-kingston-emmcs.patch new file mode 100644 index 0000000000..6f756f9fae --- /dev/null +++ b/queue-6.6/mmc-core-optimize-time-for-secure-erase-trim-for-some-kingston-emmcs.patch @@ -0,0 +1,93 @@ +From stable+bounces-244779-greg=kroah.com@vger.kernel.org Fri May 8 16:57:18 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 10:52:22 -0400 +Subject: mmc: core: Optimize time for secure erase/trim for some Kingston eMMCs +To: stable@vger.kernel.org +Cc: Luke Wang , Ulf Hansson , Sasha Levin +Message-ID: <20260508145222.1512925-1-sashal@kernel.org> + +From: Luke Wang + +[ Upstream commit d6bf2e64dec87322f2b11565ddb59c0e967f96e3 ] + +Kingston eMMC IY2964 and IB2932 takes a fixed ~2 seconds for each secure +erase/trim operation regardless of size - that is, a single secure +erase/trim operation of 1MB takes the same time as 1GB. With default +calculated 3.5MB max discard size, secure erase 1GB requires ~300 separate +operations taking ~10 minutes total. + +Add a card quirk, MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME, to set maximum +secure erase size for those devices. This allows 1GB secure erase to +complete in a single operation, reducing time from 10 minutes to just 2 +seconds. + +Signed-off-by: Luke Wang +Cc: stable@vger.kernel.org +Signed-off-by: Ulf Hansson +[ adapted `lim->max_secure_erase_sectors =` assignment to `blk_queue_max_secure_erase_sectors(q, ...)` setter and used pre-rename `mmc_can_secure_erase_trim`/`mmc_can_trim` helpers ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/mmc/core/card.h | 5 +++++ + drivers/mmc/core/queue.c | 8 ++++++-- + drivers/mmc/core/quirks.h | 9 +++++++++ + include/linux/mmc/card.h | 1 + + 4 files changed, 21 insertions(+), 2 deletions(-) + +--- a/drivers/mmc/core/card.h ++++ b/drivers/mmc/core/card.h +@@ -297,4 +297,9 @@ static inline int mmc_card_no_uhs_ddr50_ + return c->quirks & MMC_QUIRK_NO_UHS_DDR50_TUNING; + } + ++static inline int mmc_card_fixed_secure_erase_trim_time(const struct mmc_card *c) ++{ ++ return c->quirks & MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME; ++} ++ + #endif +--- a/drivers/mmc/core/queue.c ++++ b/drivers/mmc/core/queue.c +@@ -188,8 +188,12 @@ static void mmc_queue_setup_discard(stru + /* granularity must not be greater than max. discard */ + if (card->pref_erase > max_discard) + q->limits.discard_granularity = SECTOR_SIZE; +- if (mmc_can_secure_erase_trim(card)) +- blk_queue_max_secure_erase_sectors(q, max_discard); ++ if (mmc_can_secure_erase_trim(card)) { ++ if (mmc_card_fixed_secure_erase_trim_time(card)) ++ blk_queue_max_secure_erase_sectors(q, UINT_MAX >> card->erase_shift); ++ else ++ blk_queue_max_secure_erase_sectors(q, max_discard); ++ } + if (mmc_can_trim(card) && card->erased_byte == 0) + blk_queue_max_write_zeroes_sectors(q, max_discard); + } +--- a/drivers/mmc/core/quirks.h ++++ b/drivers/mmc/core/quirks.h +@@ -153,6 +153,15 @@ static const struct mmc_fixup __maybe_un + MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, + MMC_QUIRK_TRIM_BROKEN), + ++ /* ++ * On Some Kingston eMMCs, secure erase/trim time is independent ++ * of erase size, fixed at approximately 2 seconds. ++ */ ++ MMC_FIXUP("IY2964", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, ++ MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME), ++ MMC_FIXUP("IB2932", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, ++ MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME), ++ + END_FIXUP + }; + +--- a/include/linux/mmc/card.h ++++ b/include/linux/mmc/card.h +@@ -298,6 +298,7 @@ struct mmc_card { + #define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */ + #define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */ + #define MMC_QUIRK_NO_UHS_DDR50_TUNING (1<<18) /* Disable DDR50 tuning */ ++#define MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME (1<<20) /* Secure erase/trim time is fixed regardless of size */ + + bool written_flag; /* Indicates eMMC has been written since power on */ + bool reenable_cmdq; /* Re-enable Command Queue */ diff --git a/queue-6.6/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch b/queue-6.6/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch new file mode 100644 index 0000000000..0992f302c9 --- /dev/null +++ b/queue-6.6/net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch @@ -0,0 +1,176 @@ +From stable+bounces-242864-greg=kroah.com@vger.kernel.org Mon May 4 09:47:30 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 03:47:18 -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: <20260504074719.1856971-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 `*idx < cb->args[2]` instead of `*idx < 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_is_neigh_suppress_enabled(f->dst, vid)) { ++ (dst && (dst->flags & BR_PROXYARP_WIFI)) || ++ br_is_neigh_suppress_enabled(dst, vid)) { + 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 (br_is_neigh_suppress_enabled(f->dst, vid)) { ++ if (br_is_neigh_suppress_enabled(dst, vid)) { + 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 +@@ -246,6 +246,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; +@@ -258,8 +259,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; +@@ -349,7 +353,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; + } +@@ -360,7 +364,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; + } +@@ -790,6 +794,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; + unsigned long delta; +@@ -806,7 +811,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) { +@@ -818,8 +824,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)) { +@@ -940,9 +946,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 +@@ -950,10 +958,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-6.6/net-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.patch b/queue-6.6/net-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.patch new file mode 100644 index 0000000000..960b12aead --- /dev/null +++ b/queue-6.6/net-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.patch @@ -0,0 +1,90 @@ +From stable+bounces-242867-greg=kroah.com@vger.kernel.org Mon May 4 10:14:25 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 04:08:48 -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: <20260504080848.1870640-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 +@@ -26,6 +26,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 +@@ -335,6 +335,7 @@ static int mctp_route_input(struct mctp_ + unsigned long f; + u8 tag, flags; + int rc; ++ u8 ver; + + msk = NULL; + rc = -EINVAL; +@@ -357,7 +358,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); +@@ -1124,6 +1126,7 @@ static int mctp_pkttype_receive(struct s + struct mctp_skb_cb *cb; + struct mctp_route *rt; + struct mctp_hdr *mh; ++ u8 ver; + + rcu_read_lock(); + mdev = __mctp_dev_get(dev); +@@ -1141,7 +1144,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; + + /* source must be valid unicast or null; drop reserved ranges and diff --git a/queue-6.6/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch b/queue-6.6/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch new file mode 100644 index 0000000000..99f2b3ae3a --- /dev/null +++ b/queue-6.6/net-qrtr-ns-limit-the-maximum-number-of-lookups.patch @@ -0,0 +1,95 @@ +From stable+bounces-242830-greg=kroah.com@vger.kernel.org Mon May 4 06:11:20 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 00:11:11 -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: <20260504041111.1425615-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 +@@ -22,6 +22,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); +@@ -76,6 +77,11 @@ struct qrtr_node { + */ + #define QRTR_NS_MAX_SERVERS 256 + ++/* 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; +@@ -441,6 +447,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 +@@ -558,6 +565,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; +@@ -566,6 +578,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; +@@ -606,6 +619,7 @@ static void ctrl_cmd_del_lookup(struct s + + list_del(&lookup->li); + kfree(lookup); ++ qrtr_ns.lookup_count--; + } + } + diff --git a/queue-6.6/net-qrtr-ns-limit-the-maximum-server-registration-per-node.patch b/queue-6.6/net-qrtr-ns-limit-the-maximum-server-registration-per-node.patch new file mode 100644 index 0000000000..199635a1d9 --- /dev/null +++ b/queue-6.6/net-qrtr-ns-limit-the-maximum-server-registration-per-node.patch @@ -0,0 +1,109 @@ +From stable+bounces-242831-greg=kroah.com@vger.kernel.org Mon May 4 06:11:25 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 00:11:15 -0400 +Subject: net: qrtr: ns: Limit the maximum server registration per node +To: stable@vger.kernel.org +Cc: Manivannan Sadhasivam , Yiming Qian , Simon Horman , Jakub Kicinski , Sasha Levin +Message-ID: <20260504041115.1425803-1-sashal@kernel.org> + +From: Manivannan Sadhasivam + +[ Upstream commit d5ee2ff98322337951c56398e79d51815acbf955 ] + +Current code does no bound checking on the number of servers added per +node. A malicious client can flood NEW_SERVER messages and exhaust memory. + +Fix this issue by limiting the maximum number of server registrations to +256 per node. If the NEW_SERVER message is received for an old port, then +don't restrict it as it will get replaced. While at it, also rate limit +the error messages in the failure path of qrtr_ns_worker(). + +Note that the limit of 256 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") +Reported-by: Yiming Qian +Reviewed-by: Simon Horman +Signed-off-by: Manivannan Sadhasivam +Link: https://patch.msgid.link/20260409-qrtr-fix-v3-1-00a8a5ff2b51@oss.qualcomm.com +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/qrtr/ns.c | 26 +++++++++++++++++++++----- + 1 file changed, 21 insertions(+), 5 deletions(-) + +--- a/net/qrtr/ns.c ++++ b/net/qrtr/ns.c +@@ -68,8 +68,14 @@ struct qrtr_server { + struct qrtr_node { + unsigned int id; + struct xarray servers; ++ u32 server_count; + }; + ++/* Max server 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_SERVERS 256 ++ + static struct qrtr_node *node_get(unsigned int node_id) + { + struct qrtr_node *node; +@@ -227,6 +233,17 @@ static struct qrtr_server *server_add(un + if (!service || !port) + return NULL; + ++ node = node_get(node_id); ++ if (!node) ++ return NULL; ++ ++ /* Make sure the new servers per port are capped at the maximum value */ ++ old = xa_load(&node->servers, port); ++ if (!old && node->server_count >= QRTR_NS_MAX_SERVERS) { ++ pr_err_ratelimited("QRTR client node %u exceeds max server limit!\n", node_id); ++ return NULL; ++ } ++ + srv = kzalloc(sizeof(*srv), GFP_KERNEL); + if (!srv) + return NULL; +@@ -236,10 +253,6 @@ static struct qrtr_server *server_add(un + srv->node = node_id; + srv->port = port; + +- node = node_get(node_id); +- if (!node) +- goto err; +- + /* Delete the old server on the same port */ + old = xa_store(&node->servers, port, srv, GFP_KERNEL); + if (old) { +@@ -250,6 +263,8 @@ static struct qrtr_server *server_add(un + } else { + kfree(old); + } ++ } else { ++ node->server_count++; + } + + trace_qrtr_ns_server_add(srv->service, srv->instance, +@@ -290,6 +305,7 @@ static int server_del(struct qrtr_node * + } + + kfree(srv); ++ node->server_count--; + + return 0; + } +@@ -678,7 +694,7 @@ static void qrtr_ns_worker(struct work_s + } + + if (ret < 0) +- pr_err("failed while handling packet from %d:%d", ++ pr_err_ratelimited("failed while handling packet from %d:%d", + sq.sq_node, sq.sq_port); + } + diff --git a/queue-6.6/net-qrtr-ns-limit-the-total-number-of-nodes.patch b/queue-6.6/net-qrtr-ns-limit-the-total-number-of-nodes.patch new file mode 100644 index 0000000000..37a42c3e4f --- /dev/null +++ b/queue-6.6/net-qrtr-ns-limit-the-total-number-of-nodes.patch @@ -0,0 +1,77 @@ +From stable+bounces-242870-greg=kroah.com@vger.kernel.org Mon May 4 10:22:50 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 04:22:33 -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: <20260504082233.1875710-2-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 comment/define changes for missing QRTR_NS_MAX_SERVERS/LOOKUPS prereqs and kept plain kzalloc instead of kzalloc_obj ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/qrtr/ns.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/net/qrtr/ns.c ++++ b/net/qrtr/ns.c +@@ -82,6 +82,13 @@ struct qrtr_node { + */ + #define QRTR_NS_MAX_LOOKUPS 64 + ++/* Max nodes 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_NODES 64 ++ ++static u8 node_count; ++ + static struct qrtr_node *node_get(unsigned int node_id) + { + struct qrtr_node *node; +@@ -90,6 +97,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) +@@ -103,6 +115,8 @@ static struct qrtr_node *node_get(unsign + return NULL; + } + ++ node_count++; ++ + return node; + } + +@@ -406,6 +420,7 @@ static int ctrl_cmd_bye(struct sockaddr_ + delete_node: + xa_erase(&nodes, from->sq_node); + kfree(node); ++ node_count--; + + return ret; + } diff --git a/queue-6.6/pci-epf-mhi-return-0-not-remaining-timeout-when-edma-ops-complete.patch b/queue-6.6/pci-epf-mhi-return-0-not-remaining-timeout-when-edma-ops-complete.patch new file mode 100644 index 0000000000..21270a6ca7 --- /dev/null +++ b/queue-6.6/pci-epf-mhi-return-0-not-remaining-timeout-when-edma-ops-complete.patch @@ -0,0 +1,58 @@ +From stable+bounces-242425-greg=kroah.com@vger.kernel.org Fri May 1 15:13:53 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 09:13:44 -0400 +Subject: PCI: epf-mhi: Return 0, not remaining timeout, when eDMA ops complete +To: stable@vger.kernel.org +Cc: Daniel Hodges , Manivannan Sadhasivam , Krishna Chaitanya Chundru , Sasha Levin +Message-ID: <20260501131344.3233285-1-sashal@kernel.org> + +From: Daniel Hodges + +[ Upstream commit 36bfc3642b19a98f1302aed4437c331df9b481f0 ] + +pci_epf_mhi_edma_read() and pci_epf_mhi_edma_write() start DMA +operations and wait for completion with a timeout. + +On successful completion, they previously returned the remaining +timeout, which callers may treat as an error. In particular, +mhi_ep_ring_add_element(), which calls pci_epf_mhi_edma_write() via +mhi_cntrl->write_sync(), interprets any non-zero return value as +failure. + +Return 0 on success instead of the remaining timeout to prevent +mhi_ep_ring_add_element() from treating successful completion as an +error. + +Fixes: 7b99aaaddabb ("PCI: epf-mhi: Add eDMA support") +Signed-off-by: Daniel Hodges +[mani: changed commit log as per https://lore.kernel.org/linux-pci/20260227191510.GA3904799@bhelgaas] +Signed-off-by: Manivannan Sadhasivam +Reviewed-by: Krishna Chaitanya Chundru +Cc: stable@vger.kernel.org +Link: https://patch.msgid.link/20260206200529.10784-1-git@danielhodges.dev +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/pci/endpoint/functions/pci-epf-mhi.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/pci/endpoint/functions/pci-epf-mhi.c ++++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c +@@ -331,6 +331,8 @@ static int pci_epf_mhi_edma_read(struct + dev_err(dev, "DMA transfer timeout\n"); + dmaengine_terminate_sync(chan); + ret = -ETIMEDOUT; ++ } else { ++ ret = 0; + } + + err_unmap: +@@ -402,6 +404,8 @@ static int pci_epf_mhi_edma_write(struct + dev_err(dev, "DMA transfer timeout\n"); + dmaengine_terminate_sync(chan); + ret = -ETIMEDOUT; ++ } else { ++ ret = 0; + } + + err_unmap: diff --git a/queue-6.6/rdma-mana_ib-disable-rx-steering-on-rss-qp-destroy.patch b/queue-6.6/rdma-mana_ib-disable-rx-steering-on-rss-qp-destroy.patch new file mode 100644 index 0000000000..b6b7319cb0 --- /dev/null +++ b/queue-6.6/rdma-mana_ib-disable-rx-steering-on-rss-qp-destroy.patch @@ -0,0 +1,116 @@ +From stable+bounces-242579-greg=kroah.com@vger.kernel.org Sat May 2 04:59:36 2026 +From: Sasha Levin +Date: Fri, 1 May 2026 22:59:23 -0400 +Subject: RDMA/mana_ib: Disable RX steering on RSS QP destroy +To: stable@vger.kernel.org +Cc: Long Li , Leon Romanovsky , Sasha Levin +Message-ID: <20260502025930.238469-1-sashal@kernel.org> + +From: Long Li + +[ Upstream commit dbeb256e8dd87233d891b170c0b32a6466467036 ] + +When an RSS QP is destroyed (e.g. DPDK exit), mana_ib_destroy_qp_rss() +destroys the RX WQ objects but does not disable vPort RX steering in +firmware. This leaves stale steering configuration that still points to +the destroyed RX objects. + +If traffic continues to arrive (e.g. peer VM is still transmitting) and +the VF interface is subsequently brought up (mana_open), the firmware +may deliver completions using stale CQ IDs from the old RX objects. +These CQ IDs can be reused by the ethernet driver for new TX CQs, +causing RX completions to land on TX CQs: + + WARNING: mana_poll_tx_cq+0x1b8/0x220 [mana] (is_sq == false) + WARNING: mana_gd_process_eq_events+0x209/0x290 (cq_table lookup fails) + +Fix this by disabling vPort RX steering before destroying RX WQ objects. +Note that mana_fence_rqs() cannot be used here because the fence +completion is delivered on the CQ, which is polled by user-mode (e.g. +DPDK) and not visible to the kernel driver. + +Refactor the disable logic into a shared mana_disable_vport_rx() in +mana_en, exported for use by mana_ib, replacing the duplicate code. +The ethernet driver's mana_dealloc_queues() is also updated to call +this common function. + +Fixes: 0266a177631d ("RDMA/mana_ib: Add a driver for Microsoft Azure Network Adapter") +Cc: stable@vger.kernel.org +Signed-off-by: Long Li +Link: https://patch.msgid.link/20260325194100.1929056-1-longli@microsoft.com +Signed-off-by: Leon Romanovsky +[ kept early-return error handling and used unquoted NET_MANA namespace in EXPORT_SYMBOL_NS ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/infiniband/hw/mana/qp.c | 15 +++++++++++++++ + drivers/net/ethernet/microsoft/mana/mana_en.c | 11 ++++++++++- + include/net/mana/mana.h | 1 + + 3 files changed, 26 insertions(+), 1 deletion(-) + +--- a/drivers/infiniband/hw/mana/qp.c ++++ b/drivers/infiniband/hw/mana/qp.c +@@ -449,6 +449,21 @@ static int mana_ib_destroy_qp_rss(struct + ndev = mc->ports[qp->port - 1]; + mpc = netdev_priv(ndev); + ++ /* Disable vPort RX steering before destroying RX WQ objects. ++ * Otherwise firmware still routes traffic to the destroyed queues, ++ * which can cause bogus completions on reused CQ IDs when the ++ * ethernet driver later creates new queues on mana_open(). ++ * ++ * Unlike the ethernet teardown path, mana_fence_rqs() cannot be ++ * used here because the fence completion CQE is delivered on the ++ * CQ which is polled by userspace (e.g. DPDK), so there is no way ++ * for the kernel to wait for fence completion. ++ * ++ * This is best effort — if it fails there is not much we can do, ++ * and mana_cfg_vport_steering() already logs the error. ++ */ ++ mana_disable_vport_rx(mpc); ++ + for (i = 0; i < (1 << ind_tbl->log_ind_tbl_size); i++) { + ibwq = ind_tbl->ind_tbl[i]; + wq = container_of(ibwq, struct mana_ib_wq, ibwq); +--- a/drivers/net/ethernet/microsoft/mana/mana_en.c ++++ b/drivers/net/ethernet/microsoft/mana/mana_en.c +@@ -2380,6 +2380,13 @@ static void mana_rss_table_init(struct m + ethtool_rxfh_indir_default(i, apc->num_queues); + } + ++int mana_disable_vport_rx(struct mana_port_context *apc) ++{ ++ return mana_cfg_vport_steering(apc, TRI_STATE_FALSE, false, false, ++ false); ++} ++EXPORT_SYMBOL_NS(mana_disable_vport_rx, NET_MANA); ++ + int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx, + bool update_hash, bool update_tab) + { +@@ -2620,12 +2627,14 @@ static int mana_dealloc_queues(struct ne + */ + + apc->rss_state = TRI_STATE_FALSE; +- err = mana_config_rss(apc, TRI_STATE_FALSE, false, false); ++ err = mana_disable_vport_rx(apc); + if (err) { + netdev_err(ndev, "Failed to disable vPort: %d\n", err); + return err; + } + ++ mana_fence_rqs(apc); ++ + mana_destroy_vport(apc); + + return 0; +--- a/include/net/mana/mana.h ++++ b/include/net/mana/mana.h +@@ -437,6 +437,7 @@ struct mana_port_context { + netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev); + int mana_config_rss(struct mana_port_context *ac, enum TRI_STATE rx, + bool update_hash, bool update_tab); ++int mana_disable_vport_rx(struct mana_port_context *apc); + + int mana_alloc_queues(struct net_device *ndev); + int mana_attach(struct net_device *ndev); diff --git a/queue-6.6/sched-use-u64-for-bandwidth-ratio-calculations.patch b/queue-6.6/sched-use-u64-for-bandwidth-ratio-calculations.patch new file mode 100644 index 0000000000..6fdc935acf --- /dev/null +++ b/queue-6.6/sched-use-u64-for-bandwidth-ratio-calculations.patch @@ -0,0 +1,74 @@ +From stable+bounces-242625-greg=kroah.com@vger.kernel.org Sun May 3 01:30:39 2026 +From: Sasha Levin +Date: Sat, 2 May 2026 19:30:19 -0400 +Subject: sched: Use u64 for bandwidth ratio calculations +To: stable@vger.kernel.org +Cc: Joseph Salisbury , "Peter Zijlstra (Intel)" , Sasha Levin +Message-ID: <20260502233019.915219-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 +@@ -4823,7 +4823,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 +@@ -2776,7 +2776,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 +@@ -2463,7 +2463,7 @@ extern void init_dl_entity(struct sched_ + #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-6.6/series b/queue-6.6/series index a913791937..bd7d2d4a9c 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -361,3 +361,44 @@ batman-adv-stop-caching-unowned-originator-pointers-in-bat-iv.patch batman-adv-bla-prevent-use-after-free-when-deleting-claims.patch batman-adv-bla-only-purge-non-released-claims.patch batman-adv-bla-put-backbone-reference-on-failed-claim-hash-insert.patch +smb-move-some-duplicate-definitions-to-common-smbacl.h.patch +smb-common-change-the-data-type-of-num_aces-to-le16.patch +ksmbd-require-minimum-ace-size-in-smb_check_perm_dacl.patch +smb-client-validate-the-whole-dacl-before-rewriting-it-in-cifsacl.patch +f2fs-fix-uaf-caused-by-decrementing-sbi-nr_pages-in-f2fs_write_end_io.patch +ksmbd-use-msleep-instaed-of-schedule_timeout_interruptible.patch +ksmbd-replace-connection-list-with-hash-table.patch +f2fs-fix-to-do-sanity-check-on-dcc-discard_cmd_cnt-conditionally.patch +wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch +lib-test_hmm-evict-device-pages-on-file-close-to-avoid-use-after-free.patch +arm64-mm-enable-batched-tlb-flush-in-unmap_hotplug_range.patch +wifi-mt76-connac-introduce-helper-for-mt7925-chipset.patch +wifi-mt76-mt792x-describe-usb-wfsys-reset-with-a-descriptor.patch +wifi-mt76-mt792x-fix-mt7925u-usb-wfsys-reset-handling.patch +pci-epf-mhi-return-0-not-remaining-timeout-when-edma-ops-complete.patch +thermal-core-fix-thermal-zone-governor-cleanup-issues.patch +ipmi-ssif-fix-a-shutdown-race.patch +ipmi-ssif-clean-up-kthread-on-errors.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 +media-rc-igorplugusb-heed-coherency-rules.patch +block-relax-pgmap-check-in-bio_add_page-for-compatible-zone-device-pages.patch +sched-use-u64-for-bandwidth-ratio-calculations.patch +rdma-mana_ib-disable-rx-steering-on-rss-qp-destroy.patch +net-mctp-fix-don-t-require-received-header-reserved-bits-to-be-zero.patch +net-bridge-use-a-stable-fdb-dst-snapshot-in-rcu-readers.patch +net-qrtr-ns-limit-the-maximum-server-registration-per-node.patch +net-qrtr-ns-limit-the-maximum-number-of-lookups.patch +net-qrtr-ns-limit-the-total-number-of-nodes.patch +spi-fix-resource-leaks-on-device-setup-failure.patch +fbdev-defio-disconnect-deferred-i-o-from-the-lifetime-of-struct-fb_info.patch +firmware-google-framebuffer-do-not-unregister-platform-device.patch +udf-fix-partition-descriptor-append-bookkeeping.patch +mmc-core-optimize-time-for-secure-erase-trim-for-some-kingston-emmcs.patch +xfs-fix-a-resource-leak-in-xfs_alloc_buftarg.patch +hfsplus-fix-uninit-value-by-validating-catalog-record-size.patch +hfsplus-fix-held-lock-freed-on-hfsplus_fill_super.patch +wifi-rtl8xxxu-fix-potential-use-of-uninitialized-value.patch +ksmbd-reset-rcount-per-connection-in-ksmbd_conn_wait_idle_sess_id.patch diff --git a/queue-6.6/smb-client-validate-the-whole-dacl-before-rewriting-it-in-cifsacl.patch b/queue-6.6/smb-client-validate-the-whole-dacl-before-rewriting-it-in-cifsacl.patch new file mode 100644 index 0000000000..42ef3d38a7 --- /dev/null +++ b/queue-6.6/smb-client-validate-the-whole-dacl-before-rewriting-it-in-cifsacl.patch @@ -0,0 +1,229 @@ +From stable+bounces-240988-greg=kroah.com@vger.kernel.org Fri Apr 24 16:55:23 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 10:55:13 -0400 +Subject: smb: client: validate the whole DACL before rewriting it in cifsacl +To: stable@vger.kernel.org +Cc: Michael Bommarito , Steve French , Sasha Levin +Message-ID: <20260424145513.2190638-1-sashal@kernel.org> + +From: Michael Bommarito + +[ Upstream commit 0a8cf165566ba55a39fd0f4de172119dd646d39a ] + +build_sec_desc() and id_mode_to_cifs_acl() derive a DACL pointer from a +server-supplied dacloffset and then use the incoming ACL to rebuild the +chmod/chown security descriptor. + +The original fix only checked that the struct smb_acl header fits before +reading dacl_ptr->size or dacl_ptr->num_aces. That avoids the immediate +header-field OOB read, but the rewrite helpers still walk ACEs based on +pdacl->num_aces with no structural validation of the incoming DACL body. + +A malicious server can return a truncated DACL that still contains a +header, claims one or more ACEs, and then drive +replace_sids_and_copy_aces() or set_chmod_dacl() past the validated +extent while they compare or copy attacker-controlled ACEs. + +Factor the DACL structural checks into validate_dacl(), extend them to +validate each ACE against the DACL bounds, and use the shared validator +before the chmod/chown rebuild paths. parse_dacl() reuses the same +validator so the read-side parser and write-side rewrite paths agree on +what constitutes a well-formed incoming DACL. + +Fixes: bc3e9dd9d104 ("cifs: Change SIDs in ACEs while transferring file ownership.") +Cc: stable@vger.kernel.org +Assisted-by: Claude:claude-opus-4-6 +Assisted-by: Codex:gpt-5-4 +Signed-off-by: Michael Bommarito +Signed-off-by: Steve French +[ no kmalloc_objs ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/cifsacl.c | 116 +++++++++++++++++++++++++++++++++++------------- + 1 file changed, 85 insertions(+), 31 deletions(-) + +--- a/fs/smb/client/cifsacl.c ++++ b/fs/smb/client/cifsacl.c +@@ -758,6 +758,77 @@ static void dump_ace(struct smb_ace *pac + } + #endif + ++static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl) ++{ ++ int i, ace_hdr_size, ace_size, min_ace_size; ++ u16 dacl_size, num_aces; ++ char *acl_base, *end_of_dacl; ++ struct smb_ace *pace; ++ ++ if (!pdacl) ++ return 0; ++ ++ if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) { ++ cifs_dbg(VFS, "ACL too small to parse DACL\n"); ++ return -EINVAL; ++ } ++ ++ dacl_size = le16_to_cpu(pdacl->size); ++ if (dacl_size < sizeof(struct smb_acl) || ++ end_of_acl < (char *)pdacl + dacl_size) { ++ cifs_dbg(VFS, "ACL too small to parse DACL\n"); ++ return -EINVAL; ++ } ++ ++ num_aces = le16_to_cpu(pdacl->num_aces); ++ if (!num_aces) ++ return 0; ++ ++ ace_hdr_size = offsetof(struct smb_ace, sid) + ++ offsetof(struct smb_sid, sub_auth); ++ min_ace_size = ace_hdr_size + sizeof(__le32); ++ if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) { ++ cifs_dbg(VFS, "ACL too small to parse DACL\n"); ++ return -EINVAL; ++ } ++ ++ end_of_dacl = (char *)pdacl + dacl_size; ++ acl_base = (char *)pdacl; ++ ace_size = sizeof(struct smb_acl); ++ ++ for (i = 0; i < num_aces; ++i) { ++ if (end_of_dacl - acl_base < ace_size) { ++ cifs_dbg(VFS, "ACL too small to parse ACE\n"); ++ return -EINVAL; ++ } ++ ++ pace = (struct smb_ace *)(acl_base + ace_size); ++ acl_base = (char *)pace; ++ ++ if (end_of_dacl - acl_base < ace_hdr_size || ++ pace->sid.num_subauth == 0 || ++ pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) { ++ cifs_dbg(VFS, "ACL too small to parse ACE\n"); ++ return -EINVAL; ++ } ++ ++ ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth; ++ if (end_of_dacl - acl_base < ace_size || ++ le16_to_cpu(pace->size) < ace_size) { ++ cifs_dbg(VFS, "ACL too small to parse ACE\n"); ++ return -EINVAL; ++ } ++ ++ ace_size = le16_to_cpu(pace->size); ++ if (end_of_dacl - acl_base < ace_size) { ++ cifs_dbg(VFS, "ACL too small to parse ACE\n"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ + static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl, + struct smb_sid *pownersid, struct smb_sid *pgrpsid, + struct cifs_fattr *fattr, bool mode_from_special_sid) +@@ -765,7 +836,7 @@ static void parse_dacl(struct smb_acl *p + int i; + u16 num_aces = 0; + int acl_size; +- char *acl_base; ++ char *acl_base, *end_of_dacl; + struct smb_ace **ppace; + + /* BB need to add parm so we can store the SID BB */ +@@ -777,12 +848,8 @@ static void parse_dacl(struct smb_acl *p + return; + } + +- /* validate that we do not go past end of acl */ +- if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) || +- end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) { +- cifs_dbg(VFS, "ACL too small to parse DACL\n"); ++ if (validate_dacl(pdacl, end_of_acl)) + return; +- } + + cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), +@@ -793,6 +860,7 @@ static void parse_dacl(struct smb_acl *p + user/group/other have no permissions */ + fattr->cf_mode &= ~(0777); + ++ end_of_dacl = (char *)pdacl + le16_to_cpu(pdacl->size); + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + +@@ -800,36 +868,16 @@ static void parse_dacl(struct smb_acl *p + if (num_aces > 0) { + umode_t denied_mode = 0; + +- if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) / +- (offsetof(struct smb_ace, sid) + +- offsetof(struct smb_sid, sub_auth) + sizeof(__le16))) +- return; +- + ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *), + GFP_KERNEL); + if (!ppace) + return; + + for (i = 0; i < num_aces; ++i) { +- if (end_of_acl - acl_base < acl_size) +- break; +- + ppace[i] = (struct smb_ace *) (acl_base + acl_size); +- acl_base = (char *)ppace[i]; +- acl_size = offsetof(struct smb_ace, sid) + +- offsetof(struct smb_sid, sub_auth); +- +- if (end_of_acl - acl_base < acl_size || +- ppace[i]->sid.num_subauth == 0 || +- ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES || +- (end_of_acl - acl_base < +- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) || +- (le16_to_cpu(ppace[i]->size) < +- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) +- break; + + #ifdef CONFIG_CIFS_DEBUG2 +- dump_ace(ppace[i], end_of_acl); ++ dump_ace(ppace[i], end_of_dacl); + #endif + if (mode_from_special_sid && + ppace[i]->sid.num_subauth >= 3 && +@@ -872,6 +920,7 @@ static void parse_dacl(struct smb_acl *p + (void *)ppace[i], + sizeof(struct smb_ace)); */ + ++ acl_base = (char *)ppace[i]; + acl_size = le16_to_cpu(ppace[i]->size); + } + +@@ -1317,10 +1366,9 @@ static int build_sec_desc(struct smb_nts + } + + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); +- if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) { +- cifs_dbg(VFS, "Server returned illegal ACL size\n"); +- return -EINVAL; +- } ++ rc = validate_dacl(dacl_ptr, end_of_acl); ++ if (rc) ++ return rc; + } + + owner_sid_ptr = (struct smb_sid *)((char *)pntsd + +@@ -1697,6 +1745,12 @@ id_mode_to_cifs_acl(struct inode *inode, + } + + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); ++ rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen); ++ if (rc) { ++ kfree(pntsd); ++ cifs_put_tlink(tlink); ++ return rc; ++ } + if (mode_from_sid) + nsecdesclen += + le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); diff --git a/queue-6.6/smb-common-change-the-data-type-of-num_aces-to-le16.patch b/queue-6.6/smb-common-change-the-data-type-of-num_aces-to-le16.patch new file mode 100644 index 0000000000..ee7dbf44fd --- /dev/null +++ b/queue-6.6/smb-common-change-the-data-type-of-num_aces-to-le16.patch @@ -0,0 +1,296 @@ +From stable+bounces-240657-greg=kroah.com@vger.kernel.org Fri Apr 24 15:05:11 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 09:04:00 -0400 +Subject: smb: common: change the data type of num_aces to le16 +To: stable@vger.kernel.org +Cc: Namjae Jeon , Igor Leite Ladessa , Steve French , Sasha Levin +Message-ID: <20260424130401.1917926-2-sashal@kernel.org> + +From: Namjae Jeon + +[ Upstream commit 62e7dd0a39c2d0d7ff03274c36df971f1b3d2d0d ] + +2.4.5 in [MS-DTYP].pdf describe the data type of num_aces as le16. + +AceCount (2 bytes): An unsigned 16-bit integer that specifies the count +of the number of ACE records in the ACL. + +Change it to le16 and add reserved field to smb_acl struct. + +Reported-by: Igor Leite Ladessa +Tested-by: Igor Leite Ladessa +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: d07b26f39246 ("ksmbd: require minimum ACE size in smb_check_perm_dacl()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/cifsacl.c | 26 +++++++++++++------------- + fs/smb/common/smbacl.h | 3 ++- + fs/smb/server/smbacl.c | 31 ++++++++++++++++--------------- + fs/smb/server/smbacl.h | 2 +- + 4 files changed, 32 insertions(+), 30 deletions(-) + +--- a/fs/smb/client/cifsacl.c ++++ b/fs/smb/client/cifsacl.c +@@ -763,7 +763,7 @@ static void parse_dacl(struct smb_acl *p + struct cifs_fattr *fattr, bool mode_from_special_sid) + { + int i; +- int num_aces = 0; ++ u16 num_aces = 0; + int acl_size; + char *acl_base; + struct smb_ace **ppace; +@@ -786,7 +786,7 @@ static void parse_dacl(struct smb_acl *p + + cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), +- le32_to_cpu(pdacl->num_aces)); ++ le16_to_cpu(pdacl->num_aces)); + + /* reset rwx permissions for user/group/other. + Also, if num_aces is 0 i.e. DACL has no ACEs, +@@ -796,7 +796,7 @@ static void parse_dacl(struct smb_acl *p + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + +- num_aces = le32_to_cpu(pdacl->num_aces); ++ num_aces = le16_to_cpu(pdacl->num_aces); + if (num_aces > 0) { + umode_t denied_mode = 0; + +@@ -957,12 +957,12 @@ unsigned int setup_special_user_owner_AC + static void populate_new_aces(char *nacl_base, + struct smb_sid *pownersid, + struct smb_sid *pgrpsid, +- __u64 *pnmode, u32 *pnum_aces, u16 *pnsize, ++ __u64 *pnmode, u16 *pnum_aces, u16 *pnsize, + bool modefromsid, + bool posix) + { + __u64 nmode; +- u32 num_aces = 0; ++ u16 num_aces = 0; + u16 nsize = 0; + __u64 user_mode; + __u64 group_mode; +@@ -1070,7 +1070,7 @@ static __u16 replace_sids_and_copy_aces( + u16 size = 0; + struct smb_ace *pntace = NULL; + char *acl_base = NULL; +- u32 src_num_aces = 0; ++ u16 src_num_aces = 0; + u16 nsize = 0; + struct smb_ace *pnntace = NULL; + char *nacl_base = NULL; +@@ -1078,7 +1078,7 @@ static __u16 replace_sids_and_copy_aces( + + acl_base = (char *)pdacl; + size = sizeof(struct smb_acl); +- src_num_aces = le32_to_cpu(pdacl->num_aces); ++ src_num_aces = le16_to_cpu(pdacl->num_aces); + + nacl_base = (char *)pndacl; + nsize = sizeof(struct smb_acl); +@@ -1110,11 +1110,11 @@ static int set_chmod_dacl(struct smb_acl + u16 size = 0; + struct smb_ace *pntace = NULL; + char *acl_base = NULL; +- u32 src_num_aces = 0; ++ u16 src_num_aces = 0; + u16 nsize = 0; + struct smb_ace *pnntace = NULL; + char *nacl_base = NULL; +- u32 num_aces = 0; ++ u16 num_aces = 0; + bool new_aces_set = false; + + /* Assuming that pndacl and pnmode are never NULL */ +@@ -1132,7 +1132,7 @@ static int set_chmod_dacl(struct smb_acl + + acl_base = (char *)pdacl; + size = sizeof(struct smb_acl); +- src_num_aces = le32_to_cpu(pdacl->num_aces); ++ src_num_aces = le16_to_cpu(pdacl->num_aces); + + /* Retain old ACEs which we can retain */ + for (i = 0; i < src_num_aces; ++i) { +@@ -1178,7 +1178,7 @@ next_ace: + } + + finalize_dacl: +- pndacl->num_aces = cpu_to_le32(num_aces); ++ pndacl->num_aces = cpu_to_le16(num_aces); + pndacl->size = cpu_to_le16(nsize); + + return 0; +@@ -1335,7 +1335,7 @@ static int build_sec_desc(struct smb_nts + dacloffset ? dacl_ptr->revision : cpu_to_le16(ACL_REVISION); + + ndacl_ptr->size = cpu_to_le16(0); +- ndacl_ptr->num_aces = cpu_to_le32(0); ++ ndacl_ptr->num_aces = cpu_to_le16(0); + + rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, + pnmode, mode_from_sid, posix); +@@ -1699,7 +1699,7 @@ id_mode_to_cifs_acl(struct inode *inode, + dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset); + if (mode_from_sid) + nsecdesclen += +- le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); ++ le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace); + else /* cifsacl */ + nsecdesclen += le16_to_cpu(dacl_ptr->size); + } +--- a/fs/smb/common/smbacl.h ++++ b/fs/smb/common/smbacl.h +@@ -107,7 +107,8 @@ struct smb_sid { + struct smb_acl { + __le16 revision; /* revision level */ + __le16 size; +- __le32 num_aces; ++ __le16 num_aces; ++ __le16 reserved; + } __attribute__((packed)); + + struct smb_ace { +--- a/fs/smb/server/smbacl.c ++++ b/fs/smb/server/smbacl.c +@@ -338,7 +338,7 @@ void posix_state_to_acl(struct posix_acl + pace->e_perm = state->other.allow; + } + +-int init_acl_state(struct posix_acl_state *state, int cnt) ++int init_acl_state(struct posix_acl_state *state, u16 cnt) + { + int alloc; + +@@ -373,7 +373,7 @@ static void parse_dacl(struct mnt_idmap + struct smb_fattr *fattr) + { + int i, ret; +- int num_aces = 0; ++ u16 num_aces = 0; + unsigned int acl_size; + char *acl_base; + struct smb_ace **ppace; +@@ -394,12 +394,12 @@ static void parse_dacl(struct mnt_idmap + + ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n", + le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size), +- le32_to_cpu(pdacl->num_aces)); ++ le16_to_cpu(pdacl->num_aces)); + + acl_base = (char *)pdacl; + acl_size = sizeof(struct smb_acl); + +- num_aces = le32_to_cpu(pdacl->num_aces); ++ num_aces = le16_to_cpu(pdacl->num_aces); + if (num_aces <= 0) + return; + +@@ -589,7 +589,7 @@ static void parse_dacl(struct mnt_idmap + + static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap, + struct smb_ace *pndace, +- struct smb_fattr *fattr, u32 *num_aces, ++ struct smb_fattr *fattr, u16 *num_aces, + u16 *size, u32 nt_aces_num) + { + struct posix_acl_entry *pace; +@@ -717,7 +717,7 @@ static void set_ntacl_dacl(struct mnt_id + struct smb_fattr *fattr) + { + struct smb_ace *ntace, *pndace; +- int nt_num_aces = le32_to_cpu(nt_dacl->num_aces), num_aces = 0; ++ u16 nt_num_aces = le16_to_cpu(nt_dacl->num_aces), num_aces = 0; + unsigned short size = 0; + int i; + +@@ -745,7 +745,7 @@ static void set_ntacl_dacl(struct mnt_id + + set_posix_acl_entries_dacl(idmap, pndace, fattr, + &num_aces, &size, nt_num_aces); +- pndacl->num_aces = cpu_to_le32(num_aces); ++ pndacl->num_aces = cpu_to_le16(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); + } + +@@ -753,7 +753,7 @@ static void set_mode_dacl(struct mnt_idm + struct smb_acl *pndacl, struct smb_fattr *fattr) + { + struct smb_ace *pace, *pndace; +- u32 num_aces = 0; ++ u16 num_aces = 0; + u16 size = 0, ace_size = 0; + uid_t uid; + const struct smb_sid *sid; +@@ -809,7 +809,7 @@ static void set_mode_dacl(struct mnt_idm + fattr->cf_mode, 0007); + + out: +- pndacl->num_aces = cpu_to_le32(num_aces); ++ pndacl->num_aces = cpu_to_le16(num_aces); + pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); + } + +@@ -1039,8 +1039,9 @@ int smb_inherit_dacl(struct ksmbd_conn * + struct smb_sid owner_sid, group_sid; + struct dentry *parent = path->dentry->d_parent; + struct mnt_idmap *idmap = mnt_idmap(path->mnt); +- int inherited_flags = 0, flags = 0, i, ace_cnt = 0, nt_size = 0, pdacl_size; +- int rc = 0, num_aces, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; ++ int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size; ++ int rc = 0, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size; ++ u16 num_aces, ace_cnt = 0; + char *aces_base; + bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode); + +@@ -1056,7 +1057,7 @@ int smb_inherit_dacl(struct ksmbd_conn * + + parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset); + acl_len = pntsd_size - dacloffset; +- num_aces = le32_to_cpu(parent_pdacl->num_aces); ++ num_aces = le16_to_cpu(parent_pdacl->num_aces); + pntsd_type = le16_to_cpu(parent_pntsd->type); + pdacl_size = le16_to_cpu(parent_pdacl->size); + +@@ -1215,7 +1216,7 @@ pass: + pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset)); + pdacl->revision = cpu_to_le16(2); + pdacl->size = cpu_to_le16(sizeof(struct smb_acl) + nt_size); +- pdacl->num_aces = cpu_to_le32(ace_cnt); ++ pdacl->num_aces = cpu_to_le16(ace_cnt); + pace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl)); + memcpy(pace, aces_base, nt_size); + pntsd_size += sizeof(struct smb_acl) + nt_size; +@@ -1296,7 +1297,7 @@ 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++) { ++ for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); +@@ -1317,7 +1318,7 @@ 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++) { ++ for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) { + if (offsetof(struct smb_ace, access_req) > aces_size) + break; + ace_size = le16_to_cpu(ace->size); +--- a/fs/smb/server/smbacl.h ++++ b/fs/smb/server/smbacl.h +@@ -86,7 +86,7 @@ int parse_sec_desc(struct mnt_idmap *idm + int build_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd, + struct smb_ntsd *ppntsd, int ppntsd_size, int addition_info, + __u32 *secdesclen, struct smb_fattr *fattr); +-int init_acl_state(struct posix_acl_state *state, int cnt); ++int init_acl_state(struct posix_acl_state *state, u16 cnt); + void free_acl_state(struct posix_acl_state *state); + void posix_state_to_acl(struct posix_acl_state *state, + struct posix_acl_entry *pace); diff --git a/queue-6.6/smb-move-some-duplicate-definitions-to-common-smbacl.h.patch b/queue-6.6/smb-move-some-duplicate-definitions-to-common-smbacl.h.patch new file mode 100644 index 0000000000..e588e6dfd3 --- /dev/null +++ b/queue-6.6/smb-move-some-duplicate-definitions-to-common-smbacl.h.patch @@ -0,0 +1,402 @@ +From stable+bounces-240656-greg=kroah.com@vger.kernel.org Fri Apr 24 15:05:09 2026 +From: Sasha Levin +Date: Fri, 24 Apr 2026 09:03:59 -0400 +Subject: smb: move some duplicate definitions to common/smbacl.h +To: stable@vger.kernel.org +Cc: ChenXiaoSong , Namjae Jeon , Steve French , Sasha Levin +Message-ID: <20260424130401.1917926-1-sashal@kernel.org> + +From: ChenXiaoSong + +[ Upstream commit b51174da743b6b7cd87c02e882ebe60dcb99f8bf ] + +In order to maintain the code more easily, move duplicate definitions +to new common header file. + +Signed-off-by: ChenXiaoSong +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Stable-dep-of: d07b26f39246 ("ksmbd: require minimum ACE size in smb_check_perm_dacl()") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/cifsacl.h | 91 ------------------------------------ + fs/smb/common/smbacl.h | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ + fs/smb/server/smbacl.h | 111 -------------------------------------------- + 3 files changed, 123 insertions(+), 200 deletions(-) + create mode 100644 fs/smb/common/smbacl.h + +--- a/fs/smb/client/cifsacl.h ++++ b/fs/smb/client/cifsacl.h +@@ -9,8 +9,7 @@ + #ifndef _CIFSACL_H + #define _CIFSACL_H + +-#define NUM_AUTHS (6) /* number of authority fields */ +-#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ ++#include "../common/smbacl.h" + + #define READ_BIT 0x4 + #define WRITE_BIT 0x2 +@@ -23,12 +22,6 @@ + #define UBITSHIFT 6 + #define GBITSHIFT 3 + +-#define ACCESS_ALLOWED 0 +-#define ACCESS_DENIED 1 +- +-#define SIDOWNER 1 +-#define SIDGROUP 2 +- + /* + * Security Descriptor length containing DACL with 3 ACEs (one each for + * owner, group and world). +@@ -38,88 +31,6 @@ + (sizeof(struct smb_ace) * 4)) + + /* +- * Maximum size of a string representation of a SID: +- * +- * The fields are unsigned values in decimal. So: +- * +- * u8: max 3 bytes in decimal +- * u32: max 10 bytes in decimal +- * +- * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator +- * +- * For authority field, max is when all 6 values are non-zero and it must be +- * represented in hex. So "-0x" + 12 hex digits. +- * +- * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') +- */ +-#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +-#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ +- +-struct smb_ntsd { +- __le16 revision; /* revision level */ +- __le16 type; +- __le32 osidoffset; +- __le32 gsidoffset; +- __le32 sacloffset; +- __le32 dacloffset; +-} __attribute__((packed)); +- +-struct smb_sid { +- __u8 revision; /* revision level */ +- __u8 num_subauth; +- __u8 authority[NUM_AUTHS]; +- __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +-} __attribute__((packed)); +- +-/* size of a struct smb_sid, sans sub_auth array */ +-#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) +- +-struct smb_acl { +- __le16 revision; /* revision level */ +- __le16 size; +- __le32 num_aces; +-} __attribute__((packed)); +- +-/* ACE types - see MS-DTYP 2.4.4.1 */ +-#define ACCESS_ALLOWED_ACE_TYPE 0x00 +-#define ACCESS_DENIED_ACE_TYPE 0x01 +-#define SYSTEM_AUDIT_ACE_TYPE 0x02 +-#define SYSTEM_ALARM_ACE_TYPE 0x03 +-#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +-#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +-#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +-#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +-#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +-#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +-#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +-#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +-#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +-#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +-#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +-#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +-#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +-#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +-#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +-#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 +- +-/* ACE flags */ +-#define OBJECT_INHERIT_ACE 0x01 +-#define CONTAINER_INHERIT_ACE 0x02 +-#define NO_PROPAGATE_INHERIT_ACE 0x04 +-#define INHERIT_ONLY_ACE 0x08 +-#define INHERITED_ACE 0x10 +-#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +-#define FAILED_ACCESS_ACE_FLAG 0x80 +- +-struct smb_ace { +- __u8 type; /* see above and MS-DTYP 2.4.4.1 */ +- __u8 flags; +- __le16 size; +- __le32 access_req; +- struct smb_sid sid; /* ie UUID of user or group who gets these perms */ +-} __attribute__((packed)); +- +-/* + * The current SMB3 form of security descriptor is similar to what was used for + * cifs (see above) but some fields are split, and fields in the struct below + * matches names of fields to the spec, MS-DTYP (see sections 2.4.5 and +--- /dev/null ++++ b/fs/smb/common/smbacl.h +@@ -0,0 +1,121 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++/* ++ * Copyright (c) International Business Machines Corp., 2007 ++ * Author(s): Steve French (sfrench@us.ibm.com) ++ * Modified by Namjae Jeon (linkinjeon@kernel.org) ++ */ ++ ++#ifndef _COMMON_SMBACL_H ++#define _COMMON_SMBACL_H ++ ++#define NUM_AUTHS (6) /* number of authority fields */ ++#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ ++ ++/* ACE types - see MS-DTYP 2.4.4.1 */ ++#define ACCESS_ALLOWED_ACE_TYPE 0x00 ++#define ACCESS_DENIED_ACE_TYPE 0x01 ++#define SYSTEM_AUDIT_ACE_TYPE 0x02 ++#define SYSTEM_ALARM_ACE_TYPE 0x03 ++#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 ++#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 ++#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 ++#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 ++#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 ++#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 ++#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A ++#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B ++#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C ++#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D ++#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ ++#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F ++#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ ++#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 ++#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 ++#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 ++ ++/* ACE flags */ ++#define OBJECT_INHERIT_ACE 0x01 ++#define CONTAINER_INHERIT_ACE 0x02 ++#define NO_PROPAGATE_INHERIT_ACE 0x04 ++#define INHERIT_ONLY_ACE 0x08 ++#define INHERITED_ACE 0x10 ++#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 ++#define FAILED_ACCESS_ACE_FLAG 0x80 ++ ++/* ++ * Maximum size of a string representation of a SID: ++ * ++ * The fields are unsigned values in decimal. So: ++ * ++ * u8: max 3 bytes in decimal ++ * u32: max 10 bytes in decimal ++ * ++ * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator ++ * ++ * For authority field, max is when all 6 values are non-zero and it must be ++ * represented in hex. So "-0x" + 12 hex digits. ++ * ++ * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') ++ */ ++#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) ++#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ ++ ++#define DOMAIN_USER_RID_LE cpu_to_le32(513) ++ ++/* ++ * ACE types - see MS-DTYP 2.4.4.1 ++ */ ++enum { ++ ACCESS_ALLOWED, ++ ACCESS_DENIED, ++}; ++ ++/* ++ * Security ID types ++ */ ++enum { ++ SIDOWNER = 1, ++ SIDGROUP, ++ SIDCREATOR_OWNER, ++ SIDCREATOR_GROUP, ++ SIDUNIX_USER, ++ SIDUNIX_GROUP, ++ SIDNFS_USER, ++ SIDNFS_GROUP, ++ SIDNFS_MODE, ++}; ++ ++struct smb_ntsd { ++ __le16 revision; /* revision level */ ++ __le16 type; ++ __le32 osidoffset; ++ __le32 gsidoffset; ++ __le32 sacloffset; ++ __le32 dacloffset; ++} __attribute__((packed)); ++ ++struct smb_sid { ++ __u8 revision; /* revision level */ ++ __u8 num_subauth; ++ __u8 authority[NUM_AUTHS]; ++ __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ ++} __attribute__((packed)); ++ ++/* size of a struct smb_sid, sans sub_auth array */ ++#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) ++ ++struct smb_acl { ++ __le16 revision; /* revision level */ ++ __le16 size; ++ __le32 num_aces; ++} __attribute__((packed)); ++ ++struct smb_ace { ++ __u8 type; /* see above and MS-DTYP 2.4.4.1 */ ++ __u8 flags; ++ __le16 size; ++ __le32 access_req; ++ struct smb_sid sid; /* ie UUID of user or group who gets these perms */ ++} __attribute__((packed)); ++ ++#endif /* _COMMON_SMBACL_H */ +--- a/fs/smb/server/smbacl.h ++++ b/fs/smb/server/smbacl.h +@@ -8,6 +8,7 @@ + #ifndef _SMBACL_H + #define _SMBACL_H + ++#include "../common/smbacl.h" + #include + #include + #include +@@ -15,32 +16,6 @@ + + #include "mgmt/tree_connect.h" + +-#define NUM_AUTHS (6) /* number of authority fields */ +-#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */ +- +-/* +- * ACE types - see MS-DTYP 2.4.4.1 +- */ +-enum { +- ACCESS_ALLOWED, +- ACCESS_DENIED, +-}; +- +-/* +- * Security ID types +- */ +-enum { +- SIDOWNER = 1, +- SIDGROUP, +- SIDCREATOR_OWNER, +- SIDCREATOR_GROUP, +- SIDUNIX_USER, +- SIDUNIX_GROUP, +- SIDNFS_USER, +- SIDNFS_GROUP, +- SIDNFS_MODE, +-}; +- + /* Revision for ACLs */ + #define SD_REVISION 1 + +@@ -62,92 +37,8 @@ enum { + #define RM_CONTROL_VALID 0x4000 + #define SELF_RELATIVE 0x8000 + +-/* ACE types - see MS-DTYP 2.4.4.1 */ +-#define ACCESS_ALLOWED_ACE_TYPE 0x00 +-#define ACCESS_DENIED_ACE_TYPE 0x01 +-#define SYSTEM_AUDIT_ACE_TYPE 0x02 +-#define SYSTEM_ALARM_ACE_TYPE 0x03 +-#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 +-#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 +-#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06 +-#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07 +-#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08 +-#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09 +-#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A +-#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B +-#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C +-#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D +-#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */ +-#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F +-#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */ +-#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 +-#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12 +-#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13 +- +-/* ACE flags */ +-#define OBJECT_INHERIT_ACE 0x01 +-#define CONTAINER_INHERIT_ACE 0x02 +-#define NO_PROPAGATE_INHERIT_ACE 0x04 +-#define INHERIT_ONLY_ACE 0x08 +-#define INHERITED_ACE 0x10 +-#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40 +-#define FAILED_ACCESS_ACE_FLAG 0x80 +- +-/* +- * Maximum size of a string representation of a SID: +- * +- * The fields are unsigned values in decimal. So: +- * +- * u8: max 3 bytes in decimal +- * u32: max 10 bytes in decimal +- * +- * "S-" + 3 bytes for version field + 15 for authority field + NULL terminator +- * +- * For authority field, max is when all 6 values are non-zero and it must be +- * represented in hex. So "-0x" + 12 hex digits. +- * +- * Add 11 bytes for each subauthority field (10 bytes each + 1 for '-') +- */ +-#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1) +-#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */ +- +-#define DOMAIN_USER_RID_LE cpu_to_le32(513) +- + struct ksmbd_conn; + +-struct smb_ntsd { +- __le16 revision; /* revision level */ +- __le16 type; +- __le32 osidoffset; +- __le32 gsidoffset; +- __le32 sacloffset; +- __le32 dacloffset; +-} __packed; +- +-struct smb_sid { +- __u8 revision; /* revision level */ +- __u8 num_subauth; +- __u8 authority[NUM_AUTHS]; +- __le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */ +-} __packed; +- +-/* size of a struct cifs_sid, sans sub_auth array */ +-#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS) +- +-struct smb_acl { +- __le16 revision; /* revision level */ +- __le16 size; +- __le32 num_aces; +-} __packed; +- +-struct smb_ace { +- __u8 type; +- __u8 flags; +- __le16 size; +- __le32 access_req; +- struct smb_sid sid; /* ie UUID of user or group who gets these perms */ +-} __packed; +- + struct smb_fattr { + kuid_t cf_uid; + kgid_t cf_gid; diff --git a/queue-6.6/spi-fix-resource-leaks-on-device-setup-failure.patch b/queue-6.6/spi-fix-resource-leaks-on-device-setup-failure.patch new file mode 100644 index 0000000000..56245e4b56 --- /dev/null +++ b/queue-6.6/spi-fix-resource-leaks-on-device-setup-failure.patch @@ -0,0 +1,133 @@ +From stable+bounces-243011-greg=kroah.com@vger.kernel.org Mon May 4 14:16:04 2026 +From: Sasha Levin +Date: Mon, 4 May 2026 08:15:56 -0400 +Subject: spi: fix resource leaks on device setup failure +To: stable@vger.kernel.org +Cc: Johan Hovold , Saravana Kannan , Mark Brown , Sasha Levin +Message-ID: <20260504121556.2149853-1-sashal@kernel.org> + +From: Johan Hovold + +[ Upstream commit db357034f7e0cf23f233f414a8508312dfe8fbbe ] + +Make sure to call controller cleanup() if spi_setup() fails while +registering a device to avoid leaking any resources allocated by +setup(). + +Fixes: c7299fea6769 ("spi: Fix spi device unregister flow") +Cc: stable@vger.kernel.org # 5.13 +Cc: Saravana Kannan +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260410154907.129248-2-johan@kernel.org +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/spi/spi.c | 61 ++++++++++++++++++++++++++++++++---------------------- + 1 file changed, 37 insertions(+), 24 deletions(-) + +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -42,6 +42,8 @@ EXPORT_TRACEPOINT_SYMBOL(spi_transfer_st + + #include "internals.h" + ++static int __spi_setup(struct spi_device *spi, bool initial_setup); ++ + static DEFINE_IDR(spi_master_idr); + + static void spidev_release(struct device *dev) +@@ -677,7 +679,7 @@ static int __spi_add_device(struct spi_d + * normally rely on the device being setup. Devices + * using SPI_CS_HIGH can't coexist well otherwise... + */ +- status = spi_setup(spi); ++ status = __spi_setup(spi, true); + if (status < 0) { + dev_err(dev, "can't setup %s, status %d\n", + dev_name(&spi->dev), status); +@@ -3734,27 +3736,7 @@ static int spi_set_cs_timing(struct spi_ + return status; + } + +-/** +- * spi_setup - setup SPI mode and clock rate +- * @spi: the device whose settings are being modified +- * Context: can sleep, and no requests are queued to the device +- * +- * SPI protocol drivers may need to update the transfer mode if the +- * device doesn't work with its default. They may likewise need +- * to update clock rates or word sizes from initial values. This function +- * changes those settings, and must be called from a context that can sleep. +- * Except for SPI_CS_HIGH, which takes effect immediately, the changes take +- * effect the next time the device is selected and data is transferred to +- * or from it. When this function returns, the SPI device is deselected. +- * +- * Note that this call will fail if the protocol driver specifies an option +- * that the underlying controller or its driver does not support. For +- * example, not all hardware supports wire transfers using nine bit words, +- * LSB-first wire encoding, or active-high chipselects. +- * +- * Return: zero on success, else a negative error code. +- */ +-int spi_setup(struct spi_device *spi) ++static int __spi_setup(struct spi_device *spi, bool initial_setup) + { + unsigned bad_bits, ugly_bits; + int status = 0; +@@ -3833,7 +3815,7 @@ int spi_setup(struct spi_device *spi) + status = spi_set_cs_timing(spi); + if (status) { + mutex_unlock(&spi->controller->io_mutex); +- return status; ++ goto err_cleanup; + } + + if (spi->controller->auto_runtime_pm && spi->controller->set_cs) { +@@ -3842,7 +3824,7 @@ int spi_setup(struct spi_device *spi) + mutex_unlock(&spi->controller->io_mutex); + dev_err(&spi->controller->dev, "Failed to power device: %d\n", + status); +- return status; ++ goto err_cleanup; + } + + /* +@@ -3879,6 +3861,37 @@ int spi_setup(struct spi_device *spi) + status); + + return status; ++ ++err_cleanup: ++ if (initial_setup) ++ spi_cleanup(spi); ++ ++ return status; ++} ++ ++/** ++ * spi_setup - setup SPI mode and clock rate ++ * @spi: the device whose settings are being modified ++ * Context: can sleep, and no requests are queued to the device ++ * ++ * SPI protocol drivers may need to update the transfer mode if the ++ * device doesn't work with its default. They may likewise need ++ * to update clock rates or word sizes from initial values. This function ++ * changes those settings, and must be called from a context that can sleep. ++ * Except for SPI_CS_HIGH, which takes effect immediately, the changes take ++ * effect the next time the device is selected and data is transferred to ++ * or from it. When this function returns, the SPI device is deselected. ++ * ++ * Note that this call will fail if the protocol driver specifies an option ++ * that the underlying controller or its driver does not support. For ++ * example, not all hardware supports wire transfers using nine bit words, ++ * LSB-first wire encoding, or active-high chipselects. ++ * ++ * Return: zero on success, else a negative error code. ++ */ ++int spi_setup(struct spi_device *spi) ++{ ++ return __spi_setup(spi, false); + } + EXPORT_SYMBOL_GPL(spi_setup); + diff --git a/queue-6.6/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch b/queue-6.6/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch new file mode 100644 index 0000000000..8992973a10 --- /dev/null +++ b/queue-6.6/thermal-core-fix-thermal-zone-governor-cleanup-issues.patch @@ -0,0 +1,69 @@ +From stable+bounces-242186-greg=kroah.com@vger.kernel.org Thu Apr 30 19:58:55 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 13:56:55 -0400 +Subject: thermal: core: Fix thermal zone governor cleanup issues +To: stable@vger.kernel.org +Cc: "Rafael J. Wysocki" , Sasha Levin +Message-ID: <20260430175655.1906210-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 +[ kept the `thermal_zone_create_device_groups(tz, mask)` signature when adding the new failure-path cleanup ] +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 +@@ -804,6 +804,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); + mutex_destroy(&tz->lock); + complete(&tz->removal); + } else if (!strncmp(dev_name(dev), "cooling_device", +@@ -1325,8 +1326,10 @@ thermal_zone_device_register_with_trips( + /* 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); +@@ -1478,8 +1481,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_free(&thermal_tz_ida, tz->id); + ida_destroy(&tz->ida); diff --git a/queue-6.6/udf-fix-partition-descriptor-append-bookkeeping.patch b/queue-6.6/udf-fix-partition-descriptor-append-bookkeeping.patch new file mode 100644 index 0000000000..38e10e634b --- /dev/null +++ b/queue-6.6/udf-fix-partition-descriptor-append-bookkeeping.patch @@ -0,0 +1,61 @@ +From stable+bounces-244821-greg=kroah.com@vger.kernel.org Fri May 8 22:04:49 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 16:04:44 -0400 +Subject: udf: fix partition descriptor append bookkeeping +To: stable@vger.kernel.org +Cc: Seohyeon Maeng , Jan Kara , Sasha Levin +Message-ID: <20260508200444.1891021-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 +@@ -1656,8 +1656,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); +@@ -1667,6 +1668,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-6.6/wifi-mt76-connac-introduce-helper-for-mt7925-chipset.patch b/queue-6.6/wifi-mt76-connac-introduce-helper-for-mt7925-chipset.patch new file mode 100644 index 0000000000..f3b3f78110 --- /dev/null +++ b/queue-6.6/wifi-mt76-connac-introduce-helper-for-mt7925-chipset.patch @@ -0,0 +1,103 @@ +From stable+bounces-242174-greg=kroah.com@vger.kernel.org Thu Apr 30 19:23:30 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 13:23:22 -0400 +Subject: wifi: mt76: connac: introduce helper for mt7925 chipset +To: stable@vger.kernel.org +Cc: Deren Wu , Lorenzo Bianconi , Felix Fietkau , Sasha Levin +Message-ID: <20260430172324.1875442-1-sashal@kernel.org> + +From: Deren Wu + +[ Upstream commit 525209262f9c2999f6f5fa0c40b4519cd6acfa2e ] + +Introduce is_mt7925() helper for new chipset. mt7925 runs the same +firmware download and mmio map flow as mt7921. + +This is a preliminary patch to support mt7925 driver. + +Co-developed-by: Lorenzo Bianconi +Signed-off-by: Lorenzo Bianconi +Signed-off-by: Deren Wu +Signed-off-by: Felix Fietkau +Stable-dep-of: 56154fef47d1 ("wifi: mt76: mt792x: fix mt7925u USB WFSYS reset handling") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/mediatek/mt76/mt76_connac.h | 6 ++++++ + drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c | 4 ++-- + drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 3 ++- + drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h | 2 +- + 4 files changed, 11 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h ++++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h +@@ -172,6 +172,11 @@ struct mt76_connac_tx_free { + + extern const struct wiphy_wowlan_support mt76_connac_wowlan_support; + ++static inline bool is_mt7925(struct mt76_dev *dev) ++{ ++ return mt76_chip(dev) == 0x7925; ++} ++ + static inline bool is_mt7922(struct mt76_dev *dev) + { + return mt76_chip(dev) == 0x7922; +@@ -245,6 +250,7 @@ static inline bool is_mt76_fw_txp(struct + switch (mt76_chip(dev)) { + case 0x7961: + case 0x7922: ++ case 0x7925: + case 0x7663: + case 0x7622: + return false; +--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c ++++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c +@@ -170,7 +170,7 @@ void mt76_connac_write_hw_txp(struct mt7 + + txp->msdu_id[0] = cpu_to_le16(id | MT_MSDU_ID_VALID); + +- if (is_mt7663(dev) || is_mt7921(dev)) ++ if (is_mt7663(dev) || is_mt7921(dev) || is_mt7925(dev)) + last_mask = MT_TXD_LEN_LAST; + else + last_mask = MT_TXD_LEN_AMSDU_LAST | +@@ -214,7 +214,7 @@ mt76_connac_txp_skb_unmap_hw(struct mt76 + u32 last_mask; + int i; + +- if (is_mt7663(dev) || is_mt7921(dev)) ++ if (is_mt7663(dev) || is_mt7921(dev) || is_mt7925(dev)) + last_mask = MT_TXD_LEN_LAST; + else + last_mask = MT_TXD_LEN_MSDU_LAST; +--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c ++++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +@@ -66,6 +66,7 @@ int mt76_connac_mcu_init_download(struct + + if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || + (is_mt7921(dev) && addr == 0x900000) || ++ (is_mt7925(dev) && addr == 0x900000) || + (is_mt7996(dev) && addr == 0x900000)) + cmd = MCU_CMD(PATCH_START_REQ); + else +@@ -3080,7 +3081,7 @@ static u32 mt76_connac2_get_data_mode(st + { + u32 mode = DL_MODE_NEED_RSP; + +- if (!is_mt7921(dev) || info == PATCH_SEC_NOT_SUPPORT) ++ if ((!is_mt7921(dev) && !is_mt7925(dev)) || info == PATCH_SEC_NOT_SUPPORT) + return mode; + + switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) { +--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h ++++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +@@ -1739,7 +1739,7 @@ mt76_connac_mcu_gen_dl_mode(struct mt76_ + + ret |= feature_set & FW_FEATURE_SET_ENCRYPT ? + DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV : 0; +- if (is_mt7921(dev)) ++ if (is_mt7921(dev) || is_mt7925(dev)) + ret |= feature_set & FW_FEATURE_ENCRY_MODE ? + DL_CONFIG_ENCRY_MODE_SEL : 0; + ret |= FIELD_PREP(DL_MODE_KEY_IDX, diff --git a/queue-6.6/wifi-mt76-mt792x-describe-usb-wfsys-reset-with-a-descriptor.patch b/queue-6.6/wifi-mt76-mt792x-describe-usb-wfsys-reset-with-a-descriptor.patch new file mode 100644 index 0000000000..7bb9e6ed30 --- /dev/null +++ b/queue-6.6/wifi-mt76-mt792x-describe-usb-wfsys-reset-with-a-descriptor.patch @@ -0,0 +1,95 @@ +From stable+bounces-242175-greg=kroah.com@vger.kernel.org Thu Apr 30 19:25:07 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 13:23:23 -0400 +Subject: wifi: mt76: mt792x: describe USB WFSYS reset with a descriptor +To: stable@vger.kernel.org +Cc: Sean Wang , Felix Fietkau , Sasha Levin +Message-ID: <20260430172324.1875442-2-sashal@kernel.org> + +From: Sean Wang + +[ Upstream commit e6f48512c1ceebcd1ce6bb83df3b3d56a261507d ] + +Prepare mt792xu_wfsys_reset() for chips that share the same USB WFSYS +reset flow but use different register definitions. + +This is a pure refactor of the current mt7921u path and keeps the reset +sequence unchanged. + +Signed-off-by: Sean Wang +Link: https://patch.msgid.link/20260311002825.15502-1-sean.wang@kernel.org +Signed-off-by: Felix Fietkau +Stable-dep-of: 56154fef47d1 ("wifi: mt76: mt792x: fix mt7925u USB WFSYS reset handling") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 40 +++++++++++++++++++----- + 1 file changed, 32 insertions(+), 8 deletions(-) + +--- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c ++++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +@@ -208,6 +208,24 @@ static void mt792xu_epctl_rst_opt(struct + mt792xu_uhw_wr(&dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT, val); + } + ++struct mt792xu_wfsys_desc { ++ u32 rst_reg; ++ u32 done_reg; ++ u32 done_mask; ++ u32 done_val; ++ u32 delay_ms; ++ bool need_status_sel; ++}; ++ ++static const struct mt792xu_wfsys_desc mt7921_wfsys_desc = { ++ .rst_reg = MT_CBTOP_RGU_WF_SUBSYS_RST, ++ .done_reg = MT_UDMA_CONN_INFRA_STATUS, ++ .done_mask = MT_UDMA_CONN_WFSYS_INIT_DONE, ++ .done_val = MT_UDMA_CONN_WFSYS_INIT_DONE, ++ .delay_ms = 0, ++ .need_status_sel = true, ++}; ++ + int mt792xu_dma_init(struct mt792x_dev *dev, bool resume) + { + int err; +@@ -238,25 +256,31 @@ EXPORT_SYMBOL_GPL(mt792xu_dma_init); + + int mt792xu_wfsys_reset(struct mt792x_dev *dev) + { ++ const struct mt792xu_wfsys_desc *desc = &mt7921_wfsys_desc; + u32 val; + int i; + + mt792xu_epctl_rst_opt(dev, false); + +- val = mt792xu_uhw_rr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST); ++ val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg); + val |= MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; +- mt792xu_uhw_wr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val); ++ mt792xu_uhw_wr(&dev->mt76, desc->rst_reg, val); + +- usleep_range(10, 20); ++ if (desc->delay_ms) ++ msleep(desc->delay_ms); ++ else ++ usleep_range(10, 20); + +- val = mt792xu_uhw_rr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST); ++ val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg); + val &= ~MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; +- mt792xu_uhw_wr(&dev->mt76, MT_CBTOP_RGU_WF_SUBSYS_RST, val); ++ mt792xu_uhw_wr(&dev->mt76, desc->rst_reg, val); ++ ++ if (desc->need_status_sel) ++ mt792xu_uhw_wr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, 0); + +- mt792xu_uhw_wr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, 0); + for (i = 0; i < MT792x_WFSYS_INIT_RETRY_COUNT; i++) { +- val = mt792xu_uhw_rr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS); +- if (val & MT_UDMA_CONN_WFSYS_INIT_DONE) ++ val = mt792xu_uhw_rr(&dev->mt76, desc->done_reg); ++ if ((val & desc->done_mask) == desc->done_val) + break; + + msleep(100); diff --git a/queue-6.6/wifi-mt76-mt792x-fix-mt7925u-usb-wfsys-reset-handling.patch b/queue-6.6/wifi-mt76-mt792x-fix-mt7925u-usb-wfsys-reset-handling.patch new file mode 100644 index 0000000000..e13fa7ff86 --- /dev/null +++ b/queue-6.6/wifi-mt76-mt792x-fix-mt7925u-usb-wfsys-reset-handling.patch @@ -0,0 +1,73 @@ +From stable+bounces-242176-greg=kroah.com@vger.kernel.org Thu Apr 30 19:25:11 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 13:23:24 -0400 +Subject: wifi: mt76: mt792x: fix mt7925u USB WFSYS reset handling +To: stable@vger.kernel.org +Cc: Sean Wang , Felix Fietkau , Sasha Levin +Message-ID: <20260430172324.1875442-3-sashal@kernel.org> + +From: Sean Wang + +[ Upstream commit 56154fef47d104effa9f29ed3db4f805cbc0d640 ] + +mt7925u uses different reset/status registers from mt7921u. Reusing the +mt7921u register set causes the WFSYS reset to fail. + +Add a chip-specific descriptor in mt792xu_wfsys_reset() to select the +correct registers and fix mt7925u failing to initialize after a warm +reboot. + +Fixes: d28e1a48952e ("wifi: mt76: mt792x: introduce mt792x-usb module") +Cc: stable@vger.kernel.org +Signed-off-by: Sean Wang +Link: https://patch.msgid.link/20260311002825.15502-2-sean.wang@kernel.org +Signed-off-by: Felix Fietkau +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 4 ++++ + drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 13 ++++++++++++- + 2 files changed, 16 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h ++++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h +@@ -385,6 +385,10 @@ + #define MT_CBTOP_RGU_WF_SUBSYS_RST MT_CBTOP_RGU(0x600) + #define MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH BIT(0) + ++#define MT7925_CBTOP_RGU_WF_SUBSYS_RST 0x70028600 ++#define MT7925_WFSYS_INIT_DONE_ADDR 0x184c1604 ++#define MT7925_WFSYS_INIT_DONE 0x00001d1e ++ + #define MT_HW_BOUND 0x70010020 + #define MT_HW_CHIPID 0x70010200 + #define MT_HW_REV 0x70010204 +--- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c ++++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c +@@ -226,6 +226,15 @@ static const struct mt792xu_wfsys_desc m + .need_status_sel = true, + }; + ++static const struct mt792xu_wfsys_desc mt7925_wfsys_desc = { ++ .rst_reg = MT7925_CBTOP_RGU_WF_SUBSYS_RST, ++ .done_reg = MT7925_WFSYS_INIT_DONE_ADDR, ++ .done_mask = U32_MAX, ++ .done_val = MT7925_WFSYS_INIT_DONE, ++ .delay_ms = 20, ++ .need_status_sel = false, ++}; ++ + int mt792xu_dma_init(struct mt792x_dev *dev, bool resume) + { + int err; +@@ -256,7 +265,9 @@ EXPORT_SYMBOL_GPL(mt792xu_dma_init); + + int mt792xu_wfsys_reset(struct mt792x_dev *dev) + { +- const struct mt792xu_wfsys_desc *desc = &mt7921_wfsys_desc; ++ const struct mt792xu_wfsys_desc *desc = is_mt7925(&dev->mt76) ? ++ &mt7925_wfsys_desc : ++ &mt7921_wfsys_desc; + u32 val; + int i; + diff --git a/queue-6.6/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch b/queue-6.6/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch new file mode 100644 index 0000000000..807aba3d39 --- /dev/null +++ b/queue-6.6/wifi-mwifiex-fix-use-after-free-in-mwifiex_adapter_cleanup.patch @@ -0,0 +1,49 @@ +From stable+bounces-242168-greg=kroah.com@vger.kernel.org Thu Apr 30 18:58:39 2026 +From: Sasha Levin +Date: Thu, 30 Apr 2026 12:54:06 -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: <20260430165406.1860168-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()` to `del_timer_sync()` ] +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 +@@ -386,7 +386,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); + cancel_delayed_work_sync(&adapter->devdump_work); + mwifiex_cancel_all_pending_cmd(adapter); + wake_up_interruptible(&adapter->cmd_wait_q.wait); diff --git a/queue-6.6/wifi-rtl8xxxu-fix-potential-use-of-uninitialized-value.patch b/queue-6.6/wifi-rtl8xxxu-fix-potential-use-of-uninitialized-value.patch new file mode 100644 index 0000000000..88fc46314f --- /dev/null +++ b/queue-6.6/wifi-rtl8xxxu-fix-potential-use-of-uninitialized-value.patch @@ -0,0 +1,94 @@ +From stable+bounces-244855-greg=kroah.com@vger.kernel.org Sat May 9 02:35:37 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 20:35:20 -0400 +Subject: wifi: rtl8xxxu: fix potential use of uninitialized value +To: stable@vger.kernel.org +Cc: Yi Cong , Ping-Ke Shih , Sasha Levin +Message-ID: <20260509003520.2360221-1-sashal@kernel.org> + +From: Yi Cong + +[ Upstream commit f8a2fc809bfeb49130709b31a4d357a049f28547 ] + +The local variables 'mcs' and 'nss' in rtl8xxxu_update_ra_report() are +passed to rtl8xxxu_desc_to_mcsrate() as output parameters. If the helper +function encounters an unhandled rate index, it may return without setting +these values, leading to the use of uninitialized stack data. + +Remove the helper rtl8xxxu_desc_to_mcsrate() and inline the logic into +rtl8xxxu_update_ra_report(). This fixes the use of uninitialized 'mcs' +and 'nss' variables for legacy rates. + +The new implementation explicitly handles: +- Legacy rates: Set bitrate only. +- HT rates (MCS0-15): Set MCS flags, index, and NSS (1 or 2) directly. +- Invalid rates: Return early. + +Fixes: 7de16123d9e2 ("wifi: rtl8xxxu: Introduce rtl8xxxu_update_ra_report") +Cc: stable@vger.kernel.org +Suggested-by: Ping-Ke Shih +Signed-off-by: Yi Cong +Link: https://lore.kernel.org/all/96e31963da0c42dcb52ce44f818963d7@realtek.com/ +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20260306071627.56501-1-cong.yi@linux.dev +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 28 +++++------------- + 1 file changed, 8 insertions(+), 20 deletions(-) + +--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c ++++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c +@@ -4809,20 +4809,6 @@ static const struct ieee80211_rate rtl8x + {.bitrate = 540, .hw_value = 0x0b,}, + }; + +-static void rtl8xxxu_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss) +-{ +- if (rate <= DESC_RATE_54M) +- return; +- +- if (rate >= DESC_RATE_MCS0 && rate <= DESC_RATE_MCS15) { +- if (rate < DESC_RATE_MCS8) +- *nss = 1; +- else +- *nss = 2; +- *mcs = rate - DESC_RATE_MCS0; +- } +-} +- + static void rtl8xxxu_set_basic_rates(struct rtl8xxxu_priv *priv, u32 rate_cfg) + { + struct ieee80211_hw *hw = priv->hw; +@@ -4927,23 +4913,25 @@ static void rtl8xxxu_set_aifs(struct rtl + void rtl8xxxu_update_ra_report(struct rtl8xxxu_ra_report *rarpt, + u8 rate, u8 sgi, u8 bw) + { +- u8 mcs, nss; +- + rarpt->txrate.flags = 0; + + if (rate <= DESC_RATE_54M) { + rarpt->txrate.legacy = rtl8xxxu_legacy_ratetable[rate].bitrate; +- } else { +- rtl8xxxu_desc_to_mcsrate(rate, &mcs, &nss); ++ } else if (rate >= DESC_RATE_MCS0 && rate <= DESC_RATE_MCS15) { + rarpt->txrate.flags |= RATE_INFO_FLAGS_MCS; ++ if (rate < DESC_RATE_MCS8) ++ rarpt->txrate.nss = 1; ++ else ++ rarpt->txrate.nss = 2; + +- rarpt->txrate.mcs = mcs; +- rarpt->txrate.nss = nss; ++ rarpt->txrate.mcs = rate - DESC_RATE_MCS0; + + if (sgi) + rarpt->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + + rarpt->txrate.bw = bw; ++ } else { ++ return; + } + + rarpt->bit_rate = cfg80211_calculate_bitrate(&rarpt->txrate); diff --git a/queue-6.6/xfs-fix-a-resource-leak-in-xfs_alloc_buftarg.patch b/queue-6.6/xfs-fix-a-resource-leak-in-xfs_alloc_buftarg.patch new file mode 100644 index 0000000000..ffef24a66d --- /dev/null +++ b/queue-6.6/xfs-fix-a-resource-leak-in-xfs_alloc_buftarg.patch @@ -0,0 +1,37 @@ +From stable+bounces-244758-greg=kroah.com@vger.kernel.org Fri May 8 15:28:51 2026 +From: Sasha Levin +Date: Fri, 8 May 2026 09:22:52 -0400 +Subject: xfs: fix a resource leak in xfs_alloc_buftarg() +To: stable@vger.kernel.org +Cc: Haoxiang Li , "Darrick J. Wong" , Carlos Maiolino , Sasha Levin +Message-ID: <20260508132252.1479242-1-sashal@kernel.org> + +From: Haoxiang Li + +[ Upstream commit 29a7b2614357393b176ef06ba5bc3ff5afc8df69 ] + +In the error path, call fs_put_dax() to drop the DAX +device reference. + +Fixes: 6f643c57d57c ("xfs: implement ->notify_failure() for XFS") +Cc: stable@vger.kernel.org +Signed-off-by: Haoxiang Li +Reviewed-by: Darrick J. Wong +Signed-off-by: Carlos Maiolino +[ kept `kmem_free(btp)` and `return NULL` instead of `kfree(btp)`/`ERR_PTR(error)` ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + fs/xfs/xfs_buf.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/fs/xfs/xfs_buf.c ++++ b/fs/xfs/xfs_buf.c +@@ -2045,6 +2045,7 @@ error_pcpu: + error_lru: + list_lru_destroy(&btp->bt_lru); + error_free: ++ fs_put_dax(btp->bt_daxdev, mp); + kmem_free(btp); + return NULL; + }