From c8d5f77e1cf4f709b408fb04cc1997ea2c37d416 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 7 Aug 2026 14:23:52 +0200 Subject: [PATCH] 6.12-stable patches added patches: alsa-hda-codecs-hdmi-disable-keep-alive-before-audio-format-change.patch kmemleak-iommu-iova-fix-transient-kmemleak-false-positive.patch media-chips-media-wave5-support-cbp-profile.patch media-i2c-imx219-rename-vts-to-frm_length.patch media-imx219-fix-maximum-frame-length-in-lines.patch media-uapi-rkisp-correct-name-version-enum.patch mm-kmemleak-fix-checksum-computation-for-per-cpu-objects.patch mptcp-add-mptcp_userspace_pm_lookup_addr-helper.patch mptcp-pm-avoid-code-duplication-to-lookup-endp.patch mptcp-pm-use-addr-entry-for-get_local_id.patch mptcp-pm-userspace-fix-use-after-free-in-get_local_id.patch usb-gadget-f_tcm-synchronize-delayed-set_alt-with-teardown.patch usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch usb-typec-ucsi-split-connector-lock-classes.patch wifi-ath6kl-fix-use-after-free-in-aggr_reset_state.patch wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch wifi-brcmfmac-fix-43752-sdio-fwvid-incorrectly-labelled-as-cypress-cyw.patch wifi-brcmfmac-set-f2-blocksize-to-256-for-bcm43752.patch --- ...eep-alive-before-audio-format-change.patch | 130 ++++++ ...raw_spinlock_t-for-the-register-lock.patch | 27 +- ...ix-transient-kmemleak-false-positive.patch | 166 ++++++++ ...hips-media-wave5-support-cbp-profile.patch | 82 ++++ ...-i2c-imx219-rename-vts-to-frm_length.patch | 124 ++++++ ...19-fix-maximum-frame-length-in-lines.patch | 37 ++ ...uapi-rkisp-correct-name-version-enum.patch | 56 +++ ...ksum-computation-for-per-cpu-objects.patch | 78 ++++ ...ptcp_userspace_pm_lookup_addr-helper.patch | 155 +++++++ ...void-code-duplication-to-lookup-endp.patch | 71 ++++ ...p-pm-use-addr-entry-for-get_local_id.patch | 155 +++++++ ...e-fix-use-after-free-in-get_local_id.patch | 93 +++++ queue-6.12/series | 18 + ...ronize-delayed-set_alt-with-teardown.patch | 388 ++++++++++++++++++ ...-and-ordering-in-port-unregistration.patch | 173 ++++++++ ...ec-ucsi-split-connector-lock-classes.patch | 124 ++++++ ...x-use-after-free-in-aggr_reset_state.patch | 50 +++ ...ain-bus_reset-work-on-device-removal.patch | 292 +++++++++++++ ...-incorrectly-labelled-as-cypress-cyw.patch | 130 ++++++ ...set-f2-blocksize-to-256-for-bcm43752.patch | 59 +++ 20 files changed, 2392 insertions(+), 16 deletions(-) create mode 100644 queue-6.12/alsa-hda-codecs-hdmi-disable-keep-alive-before-audio-format-change.patch create mode 100644 queue-6.12/kmemleak-iommu-iova-fix-transient-kmemleak-false-positive.patch create mode 100644 queue-6.12/media-chips-media-wave5-support-cbp-profile.patch create mode 100644 queue-6.12/media-i2c-imx219-rename-vts-to-frm_length.patch create mode 100644 queue-6.12/media-imx219-fix-maximum-frame-length-in-lines.patch create mode 100644 queue-6.12/media-uapi-rkisp-correct-name-version-enum.patch create mode 100644 queue-6.12/mm-kmemleak-fix-checksum-computation-for-per-cpu-objects.patch create mode 100644 queue-6.12/mptcp-add-mptcp_userspace_pm_lookup_addr-helper.patch create mode 100644 queue-6.12/mptcp-pm-avoid-code-duplication-to-lookup-endp.patch create mode 100644 queue-6.12/mptcp-pm-use-addr-entry-for-get_local_id.patch create mode 100644 queue-6.12/mptcp-pm-userspace-fix-use-after-free-in-get_local_id.patch create mode 100644 queue-6.12/usb-gadget-f_tcm-synchronize-delayed-set_alt-with-teardown.patch create mode 100644 queue-6.12/usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch create mode 100644 queue-6.12/usb-typec-ucsi-split-connector-lock-classes.patch create mode 100644 queue-6.12/wifi-ath6kl-fix-use-after-free-in-aggr_reset_state.patch create mode 100644 queue-6.12/wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch create mode 100644 queue-6.12/wifi-brcmfmac-fix-43752-sdio-fwvid-incorrectly-labelled-as-cypress-cyw.patch create mode 100644 queue-6.12/wifi-brcmfmac-set-f2-blocksize-to-256-for-bcm43752.patch diff --git a/queue-6.12/alsa-hda-codecs-hdmi-disable-keep-alive-before-audio-format-change.patch b/queue-6.12/alsa-hda-codecs-hdmi-disable-keep-alive-before-audio-format-change.patch new file mode 100644 index 0000000000..39a319be36 --- /dev/null +++ b/queue-6.12/alsa-hda-codecs-hdmi-disable-keep-alive-before-audio-format-change.patch @@ -0,0 +1,130 @@ +From stable+bounces-296929-greg=kroah.com@vger.kernel.org Thu Aug 6 18:18:31 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 12:14:28 -0400 +Subject: ALSA: hda: codecs: hdmi: disable keep-alive before audio format change +To: stable@vger.kernel.org +Cc: Kai Vehmanen , Alexander Kaplan , Takashi Iwai , Sasha Levin +Message-ID: <20260806161428.1142408-1-sashal@kernel.org> + +From: Kai Vehmanen + +[ Upstream commit a3d6d3cedfe87bbd5a677d52b22ac20d28e59cf8 ] + +When a keep-alive (KAE) silent stream is active on an Intel HDMI/DP +codec, opening a real PCM stream reprograms the converter format and the +audio infoframe in snd_hda_hdmi_generic_pcm_prepare(). Part of that +reprogramming - the converter channel count and the channel mapping in +snd_hda_hdmi_setup_audio_infoframe() - is not safe to do while a +keep-alive stream is active. This is most visible when switching to a +multichannel PCM configuration, where the active channel count actually +changes. In that case the newly opened PCM stream plays no sound. + +Add an optional hdmi_ops .prepare hook, called at the start of the +PCM prepare sequence (before the format and infoframe are touched), and +implement it for HSW+ to release keep-alive. Keep-alive is then +re-enabled as before once the new stream has been set up, in the +setup_stream op. + +Fixes: 15175a4f2bbb ("ALSA: hda/hdmi: add keep-alive support for ADL-P and DG2") +Reported-by: Alexander Kaplan +Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8412 +Tested-by: Alexander Kaplan +Cc: +Signed-off-by: Kai Vehmanen +Link: https://patch.msgid.link/20260715180610.1371243-1-kai.vehmanen@linux.intel.com +Signed-off-by: Takashi Iwai +[ adapted three hunks from the post-6.12 split files (hdmi.c/hdmi_local.h/intelhdmi.c) back into the monolithic sound/pci/hda/patch_hdmi.c, with the prepare hook un-indented one level since 6.12 uses plain mutex_lock() instead of scoped_guard() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + sound/pci/hda/patch_hdmi.c | 48 +++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 42 insertions(+), 6 deletions(-) + +--- a/sound/pci/hda/patch_hdmi.c ++++ b/sound/pci/hda/patch_hdmi.c +@@ -111,6 +111,15 @@ struct hdmi_ops { + hda_nid_t pin_nid, int dev_id, u32 stream_tag, + int format); + ++ /* ++ * Optional hook invoked at the beginning of the PCM prepare ++ * sequence, before the audio infoframe and stream format are ++ * (re)programmed. Used to disable keep-alive / silent stream so ++ * that the format change is not done while keep-alive is active. ++ */ ++ void (*prepare)(struct hda_codec *codec, ++ struct hdmi_spec_per_pin *per_pin); ++ + void (*pin_cvt_fixup)(struct hda_codec *codec, + struct hdmi_spec_per_pin *per_pin, + hda_nid_t cvt_nid); +@@ -2134,6 +2143,9 @@ static int generic_hdmi_playback_pcm_pre + per_pin->channels = substream->runtime->channels; + per_pin->setup = true; + ++ if (spec->ops.prepare) ++ spec->ops.prepare(codec, per_pin); ++ + if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) { + stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core, + substream); +@@ -2901,6 +2913,28 @@ static void register_i915_notifier(struc + codec->relaxed_resume = 1; + } + ++/* ++ * prepare ops override for HSW+ ++ * ++ * Disable keep-alive before the converter format and audio infoframe are ++ * reprogrammed by the PCM prepare sequence. Changing the audio format (e.g. ++ * the channel count when switching to multichannel PCM) while a keep-alive ++ * stream is active is not safe, so release keep-alive here, early in the ++ * sequence. It is re-enabled once the new stream has been set up, in ++ * i915_hsw_setup_stream(). ++ */ ++static void i915_hsw_prepare(struct hda_codec *codec, ++ struct hdmi_spec_per_pin *per_pin) ++{ ++ struct hdmi_spec *spec = codec->spec; ++ ++ if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin->silent_stream) { ++ silent_stream_set_kae(codec, per_pin, false); ++ /* wait for pending transfers in codec to clear */ ++ usleep_range(100, 200); ++ } ++} ++ + /* setup_stream ops override for HSW+ */ + static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, + hda_nid_t pin_nid, int dev_id, u32 stream_tag, +@@ -2918,15 +2952,16 @@ static int i915_hsw_setup_stream(struct + + haswell_verify_D0(codec, cvt_nid, pin_nid); + +- if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { +- silent_stream_set_kae(codec, per_pin, false); +- /* wait for pending transfers in codec to clear */ +- usleep_range(100, 200); +- } +- + res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, + stream_tag, format); + ++ /* ++ * Keep-alive was disabled in i915_hsw_prepare(), re-enable it now. ++ * The pin lookup above resolves to the same per_pin that prepare ++ * used (pin_nid comes from that per_pin), so this stays balanced; a ++ * NULL per_pin only occurs on a lookup failure that also implies no ++ * active keep-alive stream to restore. ++ */ + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { + usleep_range(100, 200); + silent_stream_set_kae(codec, per_pin, true); +@@ -3100,6 +3135,7 @@ static int intel_hsw_common_init(struct + codec->depop_delay = 0; + codec->auto_runtime_pm = 1; + ++ spec->ops.prepare = i915_hsw_prepare; + spec->ops.setup_stream = i915_hsw_setup_stream; + spec->ops.pin_cvt_fixup = i915_pin_cvt_fixup; + diff --git a/queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch b/queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch index f6e0f4fa94..391e9032a8 100644 --- a/queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch +++ b/queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch @@ -54,11 +54,9 @@ Signed-off-by: Bartosz Golaszewski (cherry picked from commit a02b8950d619123da64f69b70fe1dadef217dfe4) Signed-off-by: Sasha Levin --- - drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- + drivers/gpio/gpio-pch.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) -diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c -index 63f25c72eac2f..75dd65957e4a2 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -96,7 +96,7 @@ struct pch_gpio { @@ -70,7 +68,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 }; static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) -@@ -105,7 +105,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +@@ -105,7 +105,7 @@ static void pch_gpio_set(struct gpio_chi struct pch_gpio *chip = gpiochip_get_data(gpio); unsigned long flags; @@ -79,7 +77,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 reg_val = ioread32(&chip->reg->po); if (val) reg_val |= BIT(nr); -@@ -113,7 +113,7 @@ static void pch_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +@@ -113,7 +113,7 @@ static void pch_gpio_set(struct gpio_chi reg_val &= ~BIT(nr); iowrite32(reg_val, &chip->reg->po); @@ -88,7 +86,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 } static int pch_gpio_get(struct gpio_chip *gpio, unsigned int nr) -@@ -131,7 +131,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, +@@ -131,7 +131,7 @@ static int pch_gpio_direction_output(str u32 reg_val; unsigned long flags; @@ -97,7 +95,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 reg_val = ioread32(&chip->reg->po); if (val) -@@ -145,7 +145,7 @@ static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, +@@ -145,7 +145,7 @@ static int pch_gpio_direction_output(str pm |= BIT(nr); iowrite32(pm, &chip->reg->pm); @@ -106,7 +104,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 return 0; } -@@ -156,12 +156,12 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) +@@ -156,12 +156,12 @@ static int pch_gpio_direction_input(stru u32 pm; unsigned long flags; @@ -121,7 +119,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 return 0; } -@@ -263,7 +263,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) +@@ -263,7 +263,7 @@ static int pch_irq_type(struct irq_data return 0; } @@ -130,7 +128,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 /* Set interrupt mode */ im = ioread32(im_reg) & ~(PCH_IM_MASK << (im_pos * 4)); -@@ -275,7 +275,7 @@ static int pch_irq_type(struct irq_data *d, unsigned int type) +@@ -275,7 +275,7 @@ static int pch_irq_type(struct irq_data else if (type & IRQ_TYPE_EDGE_BOTH) irq_set_handler_locked(d, handle_edge_irq); @@ -139,7 +137,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 return 0; } -@@ -372,7 +372,7 @@ static int pch_gpio_probe(struct pci_dev *pdev, +@@ -372,7 +372,7 @@ static int pch_gpio_probe(struct pci_dev chip->ioh = id->driver_data; chip->reg = chip->base; pci_set_drvdata(pdev, chip); @@ -148,7 +146,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 pch_gpio_setup(chip); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); -@@ -405,9 +405,9 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev) +@@ -405,9 +405,9 @@ static int __maybe_unused pch_gpio_suspe struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; @@ -160,7 +158,7 @@ index 63f25c72eac2f..75dd65957e4a2 100644 return 0; } -@@ -417,11 +417,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) +@@ -417,11 +417,11 @@ static int __maybe_unused pch_gpio_resum struct pch_gpio *chip = dev_get_drvdata(dev); unsigned long flags; @@ -174,6 +172,3 @@ index 63f25c72eac2f..75dd65957e4a2 100644 return 0; } --- -2.53.0 - diff --git a/queue-6.12/kmemleak-iommu-iova-fix-transient-kmemleak-false-positive.patch b/queue-6.12/kmemleak-iommu-iova-fix-transient-kmemleak-false-positive.patch new file mode 100644 index 0000000000..09d56bfcff --- /dev/null +++ b/queue-6.12/kmemleak-iommu-iova-fix-transient-kmemleak-false-positive.patch @@ -0,0 +1,166 @@ +From stable+bounces-297111-greg=kroah.com@vger.kernel.org Fri Aug 7 04:36:29 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 22:36:14 -0400 +Subject: kmemleak: iommu/iova: fix transient kmemleak false positive +To: stable@vger.kernel.org +Cc: Catalin Marinas , Ido Schimmel , Ido Schimmel , Robin Murphy , Joerg Roedel , Will Deacon , Andrew Morton , Sasha Levin +Message-ID: <20260807023615.1684238-1-sashal@kernel.org> + +From: Catalin Marinas + +[ Upstream commit 7591c127f3b17d5879f18819cad7058bf3a2e276 ] + +The introduction of iova_depot_pop() in 911aa1245da8 ("iommu/iova: Make +the rcache depot scale better") confused kmemleak by moving a struct +iova_magazine object from a singly linked list to rcache->depot and +resetting the 'next' pointer referencing it. Unlike doubly linked lists, +the content of the object being referred is never changed on removal from +a singly linked list and the kmemleak checksum heuristics do not detect +such scenario. This leads to false positives like: + +unreferenced object 0xffff8881a5301000 (size 1024): + comm "softirq", pid 0, jiffies 4306297099 (age 462.991s) + hex dump (first 32 bytes): + 00 00 00 00 00 00 00 00 e7 7d 05 00 00 00 00 00 .........}...... + 0f b4 05 00 00 00 00 00 b4 96 05 00 00 00 00 00 ................ + backtrace: + [] __kmem_cache_alloc_node+0x1e8/0x320 + [] kmalloc_trace+0x2a/0x60 + [] free_iova_fast+0x28e/0x4e0 + [] fq_ring_free_locked+0x1b0/0x310 + [] fq_flush_timeout+0x19d/0x2e0 + [] call_timer_fn+0x19a/0x5c0 + [] __run_timers+0x78b/0xb80 + [] run_timer_softirq+0x5d/0xd0 + [] __do_softirq+0x205/0x8b5 + +Introduce kmemleak_transient_leak() which resets the object checksum +requiring another scan pass before it is reported (if still unreferenced). +Call this new API in iova_depot_pop(). + +Link: https://lkml.kernel.org/r/20241104111944.2207155-1-catalin.marinas@arm.com +Link: https://lore.kernel.org/r/ZY1osaGLyT-sdKE8@shredder/ +Signed-off-by: Catalin Marinas +Reported-by: Ido Schimmel +Tested-by: Ido Schimmel +Acked-by: Robin Murphy +Cc: Joerg Roedel +Cc: Will Deacon +Signed-off-by: Andrew Morton +Stable-dep-of: 79c37ae3733e ("mm/kmemleak: fix checksum computation for per-cpu objects") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + Documentation/dev-tools/kmemleak.rst | 1 + drivers/iommu/iova.c | 6 +++++ + include/linux/kmemleak.h | 4 +++ + mm/kmemleak.c | 39 +++++++++++++++++++++++++++++++++++ + 4 files changed, 50 insertions(+) + +--- a/Documentation/dev-tools/kmemleak.rst ++++ b/Documentation/dev-tools/kmemleak.rst +@@ -161,6 +161,7 @@ See the include/linux/kmemleak.h header + - ``kmemleak_free_percpu`` - notify of a percpu memory block freeing + - ``kmemleak_update_trace`` - update object allocation stack trace + - ``kmemleak_not_leak`` - mark an object as not a leak ++- ``kmemleak_transient_leak`` - mark an object as a transient leak + - ``kmemleak_ignore`` - do not scan or report an object as leak + - ``kmemleak_scan_area`` - add scan areas inside a memory block + - ``kmemleak_no_scan`` - do not scan a memory block +--- a/drivers/iommu/iova.c ++++ b/drivers/iommu/iova.c +@@ -6,6 +6,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -673,6 +674,11 @@ static struct iova_magazine *iova_depot_ + { + struct iova_magazine *mag = rcache->depot; + ++ /* ++ * As the mag->next pointer is moved to rcache->depot and reset via ++ * the mag->size assignment, mark it as a transient false positive. ++ */ ++ kmemleak_transient_leak(mag->next); + rcache->depot = mag->next; + mag->size = IOVA_MAG_SIZE; + rcache->depot_size--; +--- a/include/linux/kmemleak.h ++++ b/include/linux/kmemleak.h +@@ -26,6 +26,7 @@ extern void kmemleak_free_part(const voi + extern void kmemleak_free_percpu(const void __percpu *ptr) __ref; + extern void kmemleak_update_trace(const void *ptr) __ref; + extern void kmemleak_not_leak(const void *ptr) __ref; ++extern void kmemleak_transient_leak(const void *ptr) __ref; + extern void kmemleak_ignore(const void *ptr) __ref; + extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref; + extern void kmemleak_no_scan(const void *ptr) __ref; +@@ -93,6 +94,9 @@ static inline void kmemleak_update_trace + static inline void kmemleak_not_leak(const void *ptr) + { + } ++static inline void kmemleak_transient_leak(const void *ptr) ++{ ++} + static inline void kmemleak_ignore(const void *ptr) + { + } +--- a/mm/kmemleak.c ++++ b/mm/kmemleak.c +@@ -951,6 +951,28 @@ static void make_black_object(unsigned l + } + + /* ++ * Reset the checksum of an object. The immediate effect is that it will not ++ * be reported as a leak during the next scan until its checksum is updated. ++ */ ++static void reset_checksum(unsigned long ptr) ++{ ++ unsigned long flags; ++ struct kmemleak_object *object; ++ ++ object = find_and_get_object(ptr, 0); ++ if (!object) { ++ kmemleak_warn("Not resetting the checksum of an unknown object at 0x%08lx\n", ++ ptr); ++ return; ++ } ++ ++ raw_spin_lock_irqsave(&object->lock, flags); ++ object->checksum = 0; ++ raw_spin_unlock_irqrestore(&object->lock, flags); ++ put_object(object); ++} ++ ++/* + * Add a scanning area to the object. If at least one such area is added, + * kmemleak will only scan these ranges rather than the whole memory block. + */ +@@ -1219,6 +1241,23 @@ void __ref kmemleak_not_leak(const void + EXPORT_SYMBOL(kmemleak_not_leak); + + /** ++ * kmemleak_transient_leak - mark an allocated object as transient false positive ++ * @ptr: pointer to beginning of the object ++ * ++ * Calling this function on an object will cause the memory block to not be ++ * reported as a leak temporarily. This may happen, for example, if the object ++ * is part of a singly linked list and the ->next reference to it is changed. ++ */ ++void __ref kmemleak_transient_leak(const void *ptr) ++{ ++ pr_debug("%s(0x%px)\n", __func__, ptr); ++ ++ if (kmemleak_enabled && ptr && !IS_ERR(ptr)) ++ reset_checksum((unsigned long)ptr); ++} ++EXPORT_SYMBOL(kmemleak_transient_leak); ++ ++/** + * kmemleak_ignore - ignore an allocated object + * @ptr: pointer to beginning of the object + * diff --git a/queue-6.12/media-chips-media-wave5-support-cbp-profile.patch b/queue-6.12/media-chips-media-wave5-support-cbp-profile.patch new file mode 100644 index 0000000000..e57f8323f4 --- /dev/null +++ b/queue-6.12/media-chips-media-wave5-support-cbp-profile.patch @@ -0,0 +1,82 @@ +From stable+bounces-295133-greg=kroah.com@vger.kernel.org Tue Aug 4 13:27:56 2026 +From: Sasha Levin +Date: Tue, 4 Aug 2026 07:21:44 -0400 +Subject: media: chips-media: wave5: Support CBP profile +To: stable@vger.kernel.org +Cc: Jackson Lee , Nas Chung , Brandon Brnich , Nicolas Dufresne , Hans Verkuil , Sasha Levin +Message-ID: <20260804112144.2939763-1-sashal@kernel.org> + +From: Jackson Lee + +[ Upstream commit f8505f9d6b223a26854003bfdba1a0772457886d ] + +Constrained Baseline Profile (CBP) and Baseline Profile (BP) have been +treated as the same. +Introduce the ability to differentiate between the two. + +Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer") +Cc: stable@vger.kernel.org +Signed-off-by: Jackson Lee +Signed-off-by: Nas Chung +Tested-by: Brandon Brnich +Reviewed-by: Nicolas Dufresne +Signed-off-by: Nicolas Dufresne +Signed-off-by: Hans Verkuil +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/platform/chips-media/wave5/wave5-hw.c | 3 +++ + drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c | 5 ++++- + drivers/media/platform/chips-media/wave5/wave5-vpuapi.h | 1 + + 3 files changed, 8 insertions(+), 1 deletion(-) + +--- a/drivers/media/platform/chips-media/wave5/wave5-hw.c ++++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c +@@ -1753,6 +1753,9 @@ int wave5_vpu_enc_init_seq(struct vpu_in + (p_param->skip_intra_trans << 25) | + (p_param->strong_intra_smooth_enable << 27) | + (p_param->en_still_picture << 30); ++ else if (inst->std == W_AVC_ENC) ++ reg_val |= (p_param->constraint_set1_flag << 29); ++ + vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val); + + reg_val = (p_param->lossless_enable) | +--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c ++++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +@@ -906,6 +906,8 @@ static int wave5_vpu_enc_s_ctrl(struct v + case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE: + inst->enc_param.profile = H264_PROFILE_BP; + inst->bit_depth = 8; ++ if (ctrl->val == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) ++ inst->enc_param.constraint_set1_flag = 1; + break; + case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN: + inst->enc_param.profile = H264_PROFILE_MP; +@@ -1168,6 +1170,7 @@ static void wave5_set_enc_openparam(stru + open_param->wave_param.intra_period = input.avc_idr_period; + } + } else { ++ open_param->wave_param.constraint_set1_flag = input.constraint_set1_flag; + open_param->wave_param.avc_idr_period = input.avc_idr_period; + } + open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode; +@@ -1631,7 +1634,7 @@ static int wave5_vpu_open_enc(struct fil + -6, 6, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, +- 0, 1, 1, 1); ++ 0, 1, 1, 0); + v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION, + 0, 1, 1, 0); +--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h ++++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h +@@ -568,6 +568,7 @@ struct enc_wave_param { + u32 lambda_scaling_enable: 1; /* enable lambda scaling using custom GOP */ + u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */ + u32 mb_level_rc_enable: 1; /* enable MB-level rate control */ ++ u32 constraint_set1_flag: 1; /* enable CBP */ + }; + + struct enc_open_param { diff --git a/queue-6.12/media-i2c-imx219-rename-vts-to-frm_length.patch b/queue-6.12/media-i2c-imx219-rename-vts-to-frm_length.patch new file mode 100644 index 0000000000..13b5af0e9b --- /dev/null +++ b/queue-6.12/media-i2c-imx219-rename-vts-to-frm_length.patch @@ -0,0 +1,124 @@ +From stable+bounces-294966-greg=kroah.com@vger.kernel.org Tue Aug 4 03:06:52 2026 +From: Sasha Levin +Date: Mon, 3 Aug 2026 21:06:41 -0400 +Subject: media: i2c: imx219: Rename VTS to FRM_LENGTH +To: stable@vger.kernel.org +Cc: Jai Luthra , Dave Stevenson , Sakari Ailus , Hans Verkuil , Sasha Levin +Message-ID: <20260804010642.2350939-1-sashal@kernel.org> + +From: Jai Luthra + +[ Upstream commit 04f78503f99ae7e9887c7fe5e4bc54a7cfb10fe0 ] + +The IMX219 datasheet refers to the vertical length + blanking as +FRM_LENGTH instead of VTS. + +Reviewed-by: Dave Stevenson +Signed-off-by: Jai Luthra +Signed-off-by: Sakari Ailus +Signed-off-by: Hans Verkuil +Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/i2c/imx219.c | 31 +++++++++++++++---------------- + 1 file changed, 15 insertions(+), 16 deletions(-) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -71,9 +71,8 @@ + #define IMX219_EXPOSURE_MAX 65535 + + /* V_TIMING internal */ +-#define IMX219_REG_VTS CCI_REG16(0x0160) +-#define IMX219_VTS_MAX 0xffff +- ++#define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) ++#define IMX219_FLL_MAX 0xffff + #define IMX219_VBLANK_MIN 32 + + /* HBLANK control - read only */ +@@ -156,7 +155,7 @@ struct imx219_mode { + unsigned int height; + + /* V-timing */ +- unsigned int vts_def; ++ unsigned int fll_def; + }; + + static const struct cci_reg_sequence imx219_common_regs[] = { +@@ -315,25 +314,25 @@ static const struct imx219_mode supporte + /* 8MPix 15fps mode */ + .width = 3280, + .height = 2464, +- .vts_def = 3526, ++ .fll_def = 3526, + }, + { + /* 1080P 30fps cropped */ + .width = 1920, + .height = 1080, +- .vts_def = 1763, ++ .fll_def = 1763, + }, + { + /* 2x2 binned 30fps mode */ + .width = 1640, + .height = 1232, +- .vts_def = 1763, ++ .fll_def = 1763, + }, + { + /* 640x480 30fps mode */ + .width = 640, + .height = 480, +- .vts_def = 1763, ++ .fll_def = 1763, + }, + }; + +@@ -444,7 +443,7 @@ static int imx219_set_ctrl(struct v4l2_c + imx219->hflip->val | imx219->vflip->val << 1, &ret); + break; + case V4L2_CID_VBLANK: +- cci_write(imx219->regmap, IMX219_REG_VTS, ++ cci_write(imx219->regmap, IMX219_REG_FRM_LENGTH_A, + format->height + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN_RED: +@@ -519,15 +518,15 @@ static int imx219_init_controls(struct i + /* Initial vblank/hblank/exposure parameters based on current mode */ + imx219->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_VBLANK, IMX219_VBLANK_MIN, +- IMX219_VTS_MAX - mode->height, 1, +- mode->vts_def - mode->height); ++ IMX219_FLL_MAX - mode->height, 1, ++ mode->fll_def - mode->height); + hblank = IMX219_PPL_DEFAULT - mode->width; + imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + if (imx219->hblank) + imx219->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; +- exposure_max = mode->vts_def - 4; ++ exposure_max = mode->fll_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops, +@@ -878,12 +877,12 @@ static int imx219_set_pad_format(struct + + /* Update limits and set FPS to default */ + __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN, +- IMX219_VTS_MAX - mode->height, 1, +- mode->vts_def - mode->height); ++ IMX219_FLL_MAX - mode->height, 1, ++ mode->fll_def - mode->height); + __v4l2_ctrl_s_ctrl(imx219->vblank, +- mode->vts_def - mode->height); ++ mode->fll_def - mode->height); + /* Update max exposure while meeting expected vblanking */ +- exposure_max = mode->vts_def - 4; ++ exposure_max = mode->fll_def - 4; + exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ? + exposure_max : IMX219_EXPOSURE_DEFAULT; + __v4l2_ctrl_modify_range(imx219->exposure, diff --git a/queue-6.12/media-imx219-fix-maximum-frame-length-in-lines.patch b/queue-6.12/media-imx219-fix-maximum-frame-length-in-lines.patch new file mode 100644 index 0000000000..974d239acc --- /dev/null +++ b/queue-6.12/media-imx219-fix-maximum-frame-length-in-lines.patch @@ -0,0 +1,37 @@ +From stable+bounces-294967-greg=kroah.com@vger.kernel.org Tue Aug 4 03:09:49 2026 +From: Sasha Levin +Date: Mon, 3 Aug 2026 21:06:42 -0400 +Subject: media: imx219: Fix maximum frame length in lines +To: stable@vger.kernel.org +Cc: Sakari Ailus , Dave Stevenson , Laurent Pinchart , Sasha Levin +Message-ID: <20260804010642.2350939-2-sashal@kernel.org> + +From: Sakari Ailus + +[ Upstream commit 2c4f1ba7354312ad2d6e34e70a518a51a9344715 ] + +The driver used the maximum frame length in lines value of 0xffff, but the +maximum appears to be 0xfffe instead. Fix it. + +Fixes: 1283b3b8f82b ("media: i2c: Add driver for Sony IMX219 sensor") +Cc: stable@vger.kernel.org +Signed-off-by: Sakari Ailus +Reviewed-by: Dave Stevenson +Reviewed-by: Laurent Pinchart +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/i2c/imx219.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/media/i2c/imx219.c ++++ b/drivers/media/i2c/imx219.c +@@ -72,7 +72,7 @@ + + /* V_TIMING internal */ + #define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160) +-#define IMX219_FLL_MAX 0xffff ++#define IMX219_FLL_MAX 0xfffe + #define IMX219_VBLANK_MIN 32 + + /* HBLANK control - read only */ diff --git a/queue-6.12/media-uapi-rkisp-correct-name-version-enum.patch b/queue-6.12/media-uapi-rkisp-correct-name-version-enum.patch new file mode 100644 index 0000000000..67293d1995 --- /dev/null +++ b/queue-6.12/media-uapi-rkisp-correct-name-version-enum.patch @@ -0,0 +1,56 @@ +From stable+bounces-296812-greg=kroah.com@vger.kernel.org Thu Aug 6 15:13:19 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:06:55 -0400 +Subject: media: uapi: rkisp: Correct name version enum +To: stable@vger.kernel.org +Cc: "Niklas Söderlund" , "Laurent Pinchart" , "Hans Verkuil" , "Sasha Levin" +Message-ID: <20260806130655.335225-1-sashal@kernel.org> + +From: Niklas Söderlund + +[ Upstream commit c4c01c4fd4a3916ffdfb35ad9f511c48e289f51c ] + +The name of the enum to hold the mapping of parameter buffer versions +have a typo in the name, correct it. While this is a uAPI header the +impact should be minimal as the enum is only used as a collection for +the one version number supported. + +Fixes: e9d05e9d5db1 ("media: uapi: rkisp1-config: Add extensible params format") +Cc: stable@vger.kernel.org +Signed-off-by: Niklas Söderlund +Reviewed-by: Laurent Pinchart +Link: https://patch.msgid.link/20260501190339.3449193-1-niklas.soderlund+renesas@ragnatech.se +Signed-off-by: Laurent Pinchart +Signed-off-by: Hans Verkuil +[ Adjusted context to keep the tree's `RKISP1_EXT_PARAM_BUFFER_V1 = 1` initializer instead of upstream's `V4L2_ISP_PARAMS_VERSION_V1`, which does not exist in this tree. ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + include/uapi/linux/rkisp1-config.h | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/include/uapi/linux/rkisp1-config.h ++++ b/include/uapi/linux/rkisp1-config.h +@@ -1487,11 +1487,11 @@ struct rkisp1_ext_params_compand_curve_c + sizeof(struct rkisp1_ext_params_compand_curve_config)) + + /** +- * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version ++ * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version + * + * @RKISP1_EXT_PARAM_BUFFER_V1: First version of RkISP1 extensible parameters + */ +-enum rksip1_ext_param_buffer_version { ++enum rkisp1_ext_param_buffer_version { + RKISP1_EXT_PARAM_BUFFER_V1 = 1, + }; + +@@ -1563,7 +1563,7 @@ enum rksip1_ext_param_buffer_version { + * +---------------------------------------------------------------------+ + * + * @version: The RkISP1 extensible parameters buffer version, see +- * :c:type:`rksip1_ext_param_buffer_version` ++ * :c:type:`rkisp1_ext_param_buffer_version` + * @data_size: The RkISP1 configuration data effective size, excluding this + * header + * @data: The RkISP1 extensible configuration data blocks diff --git a/queue-6.12/mm-kmemleak-fix-checksum-computation-for-per-cpu-objects.patch b/queue-6.12/mm-kmemleak-fix-checksum-computation-for-per-cpu-objects.patch new file mode 100644 index 0000000000..12ff291cd0 --- /dev/null +++ b/queue-6.12/mm-kmemleak-fix-checksum-computation-for-per-cpu-objects.patch @@ -0,0 +1,78 @@ +From stable+bounces-297112-greg=kroah.com@vger.kernel.org Fri Aug 7 04:37:09 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 22:36:15 -0400 +Subject: mm/kmemleak: fix checksum computation for per-cpu objects +To: stable@vger.kernel.org +Cc: Breno Leitao , Catalin Marinas , Pavel Tikhomirov , Andrew Morton , Sasha Levin +Message-ID: <20260807023615.1684238-2-sashal@kernel.org> + +From: Breno Leitao + +[ Upstream commit 79c37ae3733e93d9d8ea12ecb44f717e61439024 ] + +The per-cpu object checksum folds each CPU's CRC together with XOR and +seeds every CRC with 0. Both choices make update_checksum() miss content +changes: + + - XOR is self-cancelling, so equal contents on two CPUs cancel out and + simultaneous identical changes leave the checksum unchanged. + - crc32(0, ...) over all-zero content is 0, so a freshly allocated, + zeroed per-cpu area checksums to 0, matching the initial value, and + the object is never seen to change. + +See discussions at [0]. + +When update_checksum() wrongly reports an actively modified object as +unchanged, kmemleak stops greying it for an extra scan and can report a +live per-cpu object as a leak. + +Fold the per-cpu CRC as a single rolling checksum across all CPUs and +initialise the object checksum to ~0 so the first computed value always +registers as a change, even for content that hashes to 0. +reset_checksum() is seeded the same way. + +Link: https://lore.kernel.org/all/akfYImSNDh3OjIfR@gmail.com [0] +Link: https://lore.kernel.org/20260703-kmemleak_checksum-v1-1-5e0ab7d6966f@debian.org +Fixes: 6c99d4eb7c5e ("kmemleak: enable tracking for percpu pointers") +Signed-off-by: Breno Leitao +Co-developed-by: Catalin Marinas +Signed-off-by: Catalin Marinas +Reviewed-by: Pavel Tikhomirov +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + mm/kmemleak.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/mm/kmemleak.c ++++ b/mm/kmemleak.c +@@ -671,7 +671,7 @@ static struct kmemleak_object *__alloc_o + atomic_set(&object->use_count, 1); + object->excess_ref = 0; + object->count = 0; /* white color initially */ +- object->checksum = 0; ++ object->checksum = ~0; + object->del_state = 0; + + /* task information */ +@@ -967,7 +967,7 @@ static void reset_checksum(unsigned long + } + + raw_spin_lock_irqsave(&object->lock, flags); +- object->checksum = 0; ++ object->checksum = ~0; + raw_spin_unlock_irqrestore(&object->lock, flags); + put_object(object); + } +@@ -1382,7 +1382,8 @@ static bool update_checksum(struct kmeml + for_each_possible_cpu(cpu) { + void *ptr = per_cpu_ptr((void __percpu *)object->pointer, cpu); + +- object->checksum ^= crc32(0, kasan_reset_tag((void *)ptr), object->size); ++ object->checksum = crc32(object->checksum, ++ kasan_reset_tag((void *)ptr), object->size); + } + } else { + object->checksum = crc32(0, kasan_reset_tag((void *)object->pointer), object->size); diff --git a/queue-6.12/mptcp-add-mptcp_userspace_pm_lookup_addr-helper.patch b/queue-6.12/mptcp-add-mptcp_userspace_pm_lookup_addr-helper.patch new file mode 100644 index 0000000000..966c857944 --- /dev/null +++ b/queue-6.12/mptcp-add-mptcp_userspace_pm_lookup_addr-helper.patch @@ -0,0 +1,155 @@ +From stable+bounces-297105-greg=kroah.com@vger.kernel.org Fri Aug 7 04:36:38 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 22:36:04 -0400 +Subject: mptcp: add mptcp_userspace_pm_lookup_addr helper +To: stable@vger.kernel.org +Cc: Geliang Tang , "Matthieu Baerts (NGI0)" , Jakub Kicinski , Sasha Levin +Message-ID: <20260807023606.1683287-2-sashal@kernel.org> + +From: Geliang Tang + +[ Upstream commit e7b4083b90b7213902124d13fd1ed808360e32b1 ] + +Like __lookup_addr() helper in pm_netlink.c, a new helper +mptcp_userspace_pm_lookup_addr() is also defined in pm_userspace.c. +It looks up the corresponding mptcp_pm_addr_entry address in +userspace_pm_local_addr_list through the passed "addr" parameter +and returns the found address entry. + +This helper can be used in mptcp_userspace_pm_delete_local_addr(), +mptcp_userspace_pm_set_flags(), mptcp_userspace_pm_get_local_id() +and mptcp_userspace_pm_is_backup() to simplify the code. + +Please note that with this change now list_for_each_entry() is used in +mptcp_userspace_pm_append_new_local_addr(), not list_for_each_entry_safe(), +but that's OK to do so because mptcp_userspace_pm_lookup_addr() only +returns an entry from the list, the list hasn't been modified here. + +Signed-off-by: Geliang Tang +Reviewed-by: Matthieu Baerts (NGI0) +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20241213-net-next-mptcp-pm-misc-cleanup-v1-1-ddb6d00109a8@kernel.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/mptcp/pm_userspace.c | 71 +++++++++++++++++++++++------------------------ + 1 file changed, 36 insertions(+), 35 deletions(-) + +--- a/net/mptcp/pm_userspace.c ++++ b/net/mptcp/pm_userspace.c +@@ -26,6 +26,19 @@ void mptcp_free_local_addr_list(struct m + } + } + ++static struct mptcp_pm_addr_entry * ++mptcp_userspace_pm_lookup_addr(struct mptcp_sock *msk, ++ const struct mptcp_addr_info *addr) ++{ ++ struct mptcp_pm_addr_entry *entry; ++ ++ list_for_each_entry(entry, &msk->pm.userspace_pm_local_addr_list, list) { ++ if (mptcp_addresses_equal(&entry->addr, addr, false)) ++ return entry; ++ } ++ return NULL; ++} ++ + static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, + struct mptcp_pm_addr_entry *entry, + bool needs_id) +@@ -90,22 +103,20 @@ append_err: + static int mptcp_userspace_pm_delete_local_addr(struct mptcp_sock *msk, + struct mptcp_pm_addr_entry *addr) + { +- struct mptcp_pm_addr_entry *entry, *tmp; + struct sock *sk = (struct sock *)msk; ++ struct mptcp_pm_addr_entry *entry; + +- list_for_each_entry_safe(entry, tmp, &msk->pm.userspace_pm_local_addr_list, list) { +- if (mptcp_addresses_equal(&entry->addr, &addr->addr, false)) { +- /* TODO: a refcount is needed because the entry can +- * be used multiple times (e.g. fullmesh mode). +- */ +- list_del_rcu(&entry->list); +- sock_kfree_s(sk, entry, sizeof(*entry)); +- msk->pm.local_addr_used--; +- return 0; +- } +- } +- +- return -EINVAL; ++ entry = mptcp_userspace_pm_lookup_addr(msk, &addr->addr); ++ if (!entry) ++ return -EINVAL; ++ ++ /* TODO: a refcount is needed because the entry can ++ * be used multiple times (e.g. fullmesh mode). ++ */ ++ list_del_rcu(&entry->list); ++ sock_kfree_s(sk, entry, sizeof(*entry)); ++ msk->pm.local_addr_used--; ++ return 0; + } + + static struct mptcp_pm_addr_entry * +@@ -123,17 +134,12 @@ mptcp_userspace_pm_lookup_addr_by_id(str + int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, + struct mptcp_addr_info *skc) + { +- struct mptcp_pm_addr_entry *entry = NULL, *e, new_entry; ++ struct mptcp_pm_addr_entry *entry = NULL, new_entry; + __be16 msk_sport = ((struct inet_sock *) + inet_sk((struct sock *)msk))->inet_sport; + + spin_lock_bh(&msk->pm.lock); +- list_for_each_entry(e, &msk->pm.userspace_pm_local_addr_list, list) { +- if (mptcp_addresses_equal(&e->addr, skc, false)) { +- entry = e; +- break; +- } +- } ++ entry = mptcp_userspace_pm_lookup_addr(msk, skc); + spin_unlock_bh(&msk->pm.lock); + if (entry) + return entry->addr.id; +@@ -153,15 +159,11 @@ bool mptcp_userspace_pm_is_backup(struct + struct mptcp_addr_info *skc) + { + struct mptcp_pm_addr_entry *entry; +- bool backup = false; ++ bool backup; + + spin_lock_bh(&msk->pm.lock); +- list_for_each_entry(entry, &msk->pm.userspace_pm_local_addr_list, list) { +- if (mptcp_addresses_equal(&entry->addr, skc, false)) { +- backup = !!(entry->flags & MPTCP_PM_ADDR_FLAG_BACKUP); +- break; +- } +- } ++ entry = mptcp_userspace_pm_lookup_addr(msk, skc); ++ backup = entry && !!(entry->flags & MPTCP_PM_ADDR_FLAG_BACKUP); + spin_unlock_bh(&msk->pm.lock); + + return backup; +@@ -607,13 +609,12 @@ int mptcp_userspace_pm_set_flags(struct + bkup = 1; + + spin_lock_bh(&msk->pm.lock); +- list_for_each_entry(entry, &msk->pm.userspace_pm_local_addr_list, list) { +- if (mptcp_addresses_equal(&entry->addr, &loc.addr, false)) { +- if (bkup) +- entry->flags |= MPTCP_PM_ADDR_FLAG_BACKUP; +- else +- entry->flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP; +- } ++ entry = mptcp_userspace_pm_lookup_addr(msk, &loc.addr); ++ if (entry) { ++ if (bkup) ++ entry->flags |= MPTCP_PM_ADDR_FLAG_BACKUP; ++ else ++ entry->flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP; + } + spin_unlock_bh(&msk->pm.lock); + diff --git a/queue-6.12/mptcp-pm-avoid-code-duplication-to-lookup-endp.patch b/queue-6.12/mptcp-pm-avoid-code-duplication-to-lookup-endp.patch new file mode 100644 index 0000000000..d9e445097d --- /dev/null +++ b/queue-6.12/mptcp-pm-avoid-code-duplication-to-lookup-endp.patch @@ -0,0 +1,71 @@ +From stable+bounces-297104-greg=kroah.com@vger.kernel.org Fri Aug 7 04:36:36 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 22:36:03 -0400 +Subject: mptcp: pm: avoid code duplication to lookup endp +To: stable@vger.kernel.org +Cc: Geliang Tang , "Matthieu Baerts (NGI0)" , Jakub Kicinski , Sasha Levin +Message-ID: <20260807023606.1683287-1-sashal@kernel.org> + +From: Geliang Tang + +[ Upstream commit 1d7fa6ceb91fddbe38cae3521d5d1075bce6a00e ] + +The helper __lookup_addr() can be used in mptcp_pm_nl_get_local_id() +and mptcp_pm_nl_is_backup() to simplify the code, and avoid code +duplication. + +Co-developed-by: Matthieu Baerts (NGI0) +Signed-off-by: Matthieu Baerts (NGI0) +Signed-off-by: Geliang Tang +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20241115-net-next-mptcp-pm-lockless-dump-v1-2-f4a1bcb4ca2c@kernel.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/mptcp/pm_netlink.c | 20 ++++++-------------- + 1 file changed, 6 insertions(+), 14 deletions(-) + +--- a/net/mptcp/pm_netlink.c ++++ b/net/mptcp/pm_netlink.c +@@ -1278,17 +1278,13 @@ int mptcp_pm_nl_get_local_id(struct mptc + { + struct mptcp_pm_addr_entry *entry; + struct pm_nl_pernet *pernet; +- int ret = -1; ++ int ret; + + pernet = pm_nl_get_pernet_from_msk(msk); + + rcu_read_lock(); +- list_for_each_entry_rcu(entry, &pernet->local_addr_list, list) { +- if (mptcp_addresses_equal(&entry->addr, skc, entry->addr.port)) { +- ret = entry->addr.id; +- break; +- } +- } ++ entry = __lookup_addr(pernet, skc); ++ ret = entry ? entry->addr.id : -1; + rcu_read_unlock(); + if (ret >= 0) + return ret; +@@ -1315,15 +1311,11 @@ bool mptcp_pm_nl_is_backup(struct mptcp_ + { + struct pm_nl_pernet *pernet = pm_nl_get_pernet_from_msk(msk); + struct mptcp_pm_addr_entry *entry; +- bool backup = false; ++ bool backup; + + rcu_read_lock(); +- list_for_each_entry_rcu(entry, &pernet->local_addr_list, list) { +- if (mptcp_addresses_equal(&entry->addr, skc, entry->addr.port)) { +- backup = !!(entry->flags & MPTCP_PM_ADDR_FLAG_BACKUP); +- break; +- } +- } ++ entry = __lookup_addr(pernet, skc); ++ backup = entry && !!(entry->flags & MPTCP_PM_ADDR_FLAG_BACKUP); + rcu_read_unlock(); + + return backup; diff --git a/queue-6.12/mptcp-pm-use-addr-entry-for-get_local_id.patch b/queue-6.12/mptcp-pm-use-addr-entry-for-get_local_id.patch new file mode 100644 index 0000000000..931b6f09d5 --- /dev/null +++ b/queue-6.12/mptcp-pm-use-addr-entry-for-get_local_id.patch @@ -0,0 +1,155 @@ +From stable+bounces-297106-greg=kroah.com@vger.kernel.org Fri Aug 7 04:36:18 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 22:36:05 -0400 +Subject: mptcp: pm: use addr entry for get_local_id +To: stable@vger.kernel.org +Cc: Geliang Tang , "Matthieu Baerts (NGI0)" , Jakub Kicinski , Sasha Levin +Message-ID: <20260807023606.1683287-3-sashal@kernel.org> + +From: Geliang Tang + +[ Upstream commit 7462fe22cc74321eb663768848976d42eba3ddbb ] + +The following code in mptcp_userspace_pm_get_local_id() that assigns "skc" +to "new_entry" is not allowed in BPF if we use the same code to implement +the get_local_id() interface of a BFP path manager: + + memset(&new_entry, 0, sizeof(struct mptcp_pm_addr_entry)); + new_entry.addr = *skc; + new_entry.addr.id = 0; + new_entry.flags = MPTCP_PM_ADDR_FLAG_IMPLICIT; + +To solve the issue, this patch moves this assignment to "new_entry" forward +to mptcp_pm_get_local_id(), and then passing "new_entry" as a parameter to +both mptcp_pm_nl_get_local_id() and mptcp_userspace_pm_get_local_id(). + +No behavioural changes intended. + +Signed-off-by: Geliang Tang +Reviewed-by: Matthieu Baerts (NGI0) +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20250307-net-next-mptcp-pm-reorg-v1-1-abef20ada03b@kernel.org +Signed-off-by: Jakub Kicinski +Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/mptcp/pm.c | 9 ++++++--- + net/mptcp/pm_netlink.c | 11 ++++------- + net/mptcp/pm_userspace.c | 17 ++++++----------- + net/mptcp/protocol.h | 6 ++++-- + 4 files changed, 20 insertions(+), 23 deletions(-) + +--- a/net/mptcp/pm.c ++++ b/net/mptcp/pm.c +@@ -427,7 +427,7 @@ out_unlock: + + int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc) + { +- struct mptcp_addr_info skc_local; ++ struct mptcp_pm_addr_entry skc_local = { 0 }; + struct mptcp_addr_info msk_local; + + if (WARN_ON_ONCE(!msk)) +@@ -437,10 +437,13 @@ int mptcp_pm_get_local_id(struct mptcp_s + * addr + */ + mptcp_local_address((struct sock_common *)msk, &msk_local); +- mptcp_local_address((struct sock_common *)skc, &skc_local); +- if (mptcp_addresses_equal(&msk_local, &skc_local, false)) ++ mptcp_local_address((struct sock_common *)skc, &skc_local.addr); ++ if (mptcp_addresses_equal(&msk_local, &skc_local.addr, false)) + return 0; + ++ skc_local.addr.id = 0; ++ skc_local.flags = MPTCP_PM_ADDR_FLAG_IMPLICIT; ++ + if (mptcp_pm_is_userspace(msk)) + return mptcp_userspace_pm_get_local_id(msk, &skc_local); + return mptcp_pm_nl_get_local_id(msk, &skc_local); +--- a/net/mptcp/pm_netlink.c ++++ b/net/mptcp/pm_netlink.c +@@ -1274,7 +1274,8 @@ static int mptcp_pm_nl_create_listen_soc + return err; + } + +-int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc) ++int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, ++ struct mptcp_pm_addr_entry *skc) + { + struct mptcp_pm_addr_entry *entry; + struct pm_nl_pernet *pernet; +@@ -1283,7 +1284,7 @@ int mptcp_pm_nl_get_local_id(struct mptc + pernet = pm_nl_get_pernet_from_msk(msk); + + rcu_read_lock(); +- entry = __lookup_addr(pernet, skc); ++ entry = __lookup_addr(pernet, &skc->addr); + ret = entry ? entry->addr.id : -1; + rcu_read_unlock(); + if (ret >= 0) +@@ -1294,12 +1295,8 @@ int mptcp_pm_nl_get_local_id(struct mptc + if (!entry) + return -ENOMEM; + +- entry->addr = *skc; +- entry->addr.id = 0; ++ *entry = *skc; + entry->addr.port = 0; +- entry->ifindex = 0; +- entry->flags = MPTCP_PM_ADDR_FLAG_IMPLICIT; +- entry->lsk = NULL; + ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, false); + if (ret < 0) + kfree(entry); +--- a/net/mptcp/pm_userspace.c ++++ b/net/mptcp/pm_userspace.c +@@ -132,27 +132,22 @@ mptcp_userspace_pm_lookup_addr_by_id(str + } + + int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, +- struct mptcp_addr_info *skc) ++ struct mptcp_pm_addr_entry *skc) + { +- struct mptcp_pm_addr_entry *entry = NULL, new_entry; + __be16 msk_sport = ((struct inet_sock *) + inet_sk((struct sock *)msk))->inet_sport; ++ struct mptcp_pm_addr_entry *entry; + + spin_lock_bh(&msk->pm.lock); +- entry = mptcp_userspace_pm_lookup_addr(msk, skc); ++ entry = mptcp_userspace_pm_lookup_addr(msk, &skc->addr); + spin_unlock_bh(&msk->pm.lock); + if (entry) + return entry->addr.id; + +- memset(&new_entry, 0, sizeof(struct mptcp_pm_addr_entry)); +- new_entry.addr = *skc; +- new_entry.addr.id = 0; +- new_entry.flags = MPTCP_PM_ADDR_FLAG_IMPLICIT; ++ if (skc->addr.port == msk_sport) ++ skc->addr.port = 0; + +- if (new_entry.addr.port == msk_sport) +- new_entry.addr.port = 0; +- +- return mptcp_userspace_pm_append_new_local_addr(msk, &new_entry, true); ++ return mptcp_userspace_pm_append_new_local_addr(msk, skc, true); + } + + bool mptcp_userspace_pm_is_backup(struct mptcp_sock *msk, +--- a/net/mptcp/protocol.h ++++ b/net/mptcp/protocol.h +@@ -1136,8 +1136,10 @@ bool mptcp_pm_add_addr_signal(struct mpt + bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining, + struct mptcp_rm_list *rm_list); + int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); +-int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc); +-int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc); ++int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, ++ struct mptcp_pm_addr_entry *skc); ++int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, ++ struct mptcp_pm_addr_entry *skc); + bool mptcp_pm_is_backup(struct mptcp_sock *msk, struct sock_common *skc); + bool mptcp_pm_nl_is_backup(struct mptcp_sock *msk, struct mptcp_addr_info *skc); + bool mptcp_userspace_pm_is_backup(struct mptcp_sock *msk, struct mptcp_addr_info *skc); diff --git a/queue-6.12/mptcp-pm-userspace-fix-use-after-free-in-get_local_id.patch b/queue-6.12/mptcp-pm-userspace-fix-use-after-free-in-get_local_id.patch new file mode 100644 index 0000000000..07cf879b7e --- /dev/null +++ b/queue-6.12/mptcp-pm-userspace-fix-use-after-free-in-get_local_id.patch @@ -0,0 +1,93 @@ +From stable+bounces-297107-greg=kroah.com@vger.kernel.org Fri Aug 7 04:36:20 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 22:36:06 -0400 +Subject: mptcp: pm: userspace: fix use-after-free in get_local_id +To: stable@vger.kernel.org +Cc: Geliang Tang , Xuanqiang Luo , "Matthieu Baerts (NGI0)" , Jakub Kicinski , Sasha Levin +Message-ID: <20260807023606.1683287-4-sashal@kernel.org> + +From: Geliang Tang + +[ Upstream commit 9bc6d5e4ca9f3cbb41d43400b3a31cb0403796c9 ] + +In mptcp_pm_userspace_get_local_id(), the address entry is looked up under +spinlock, but its id is read after dropping the lock. A concurrent deletion +can free the entry between the unlock and the read, leading to UAF. + +The race window is narrow. It was reproduced only with a locally +constructed stress test that repeatedly overlaps an MP_JOIN SYN with a +MPTCP_PM_CMD_SUBFLOW_DESTROY request. + +However, the KASAN report below confirms that the race is reachable: + + [ 666.319376] BUG: KASAN: slab-use-after-free in mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 + [ 666.319386] Read of size 1 at addr ffff888124845610 by task swapper/0/0 + ... + [ 666.319401] Call Trace: + [ 666.319405] + [ 666.319408] dump_stack_lvl+0x53/0x70 + [ 666.319412] print_address_description.constprop.0+0x2c/0x3b0 + [ 666.319418] print_report+0xbe/0x2b0 + [ 666.319421] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 + [ 666.319423] kasan_report+0xce/0x100 + [ 666.319426] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 + [ 666.319429] mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 + [ 666.319433] mptcp_pm_get_local_id+0x371/0x440 + ... + [ 666.319821] Allocated by task 45539: + [ 666.319844] kasan_save_stack+0x33/0x60 + [ 666.319855] kasan_save_track+0x14/0x30 + [ 666.319858] __kasan_kmalloc+0x8f/0xa0 + [ 666.319863] __kmalloc_noprof+0x1e7/0x520 + [ 666.319867] sock_kmalloc+0xdf/0x130 + [ 666.319885] sock_kmemdup+0x1b/0x40 + [ 666.319888] mptcp_userspace_pm_append_new_local_addr+0x261/0x500 + [ 666.319910] mptcp_pm_nl_announce_doit+0x16a/0x610 + ... + [ 666.319967] Freed by task 45560: + [ 666.319988] kasan_save_stack+0x33/0x60 + [ 666.319991] kasan_save_track+0x14/0x30 + [ 666.319994] kasan_save_free_info+0x3b/0x60 + [ 666.319998] __kasan_slab_free+0x43/0x70 + [ 666.320000] kfree+0x166/0x440 + [ 666.320003] sock_kfree_s+0x1d/0x50 + [ 666.320007] mptcp_userspace_pm_delete_local_addr.isra.0+0x157/0x200 + [ 666.320011] mptcp_pm_nl_subflow_destroy_doit+0x51d/0xea0 + +Fix by copying the id into a local variable while still holding the lock, +and use -1 as a "not found" sentinel. + +Fixes: f012d796a6de ("mptcp: check addrs list in userspace_pm_get_local_id") +Cc: stable@vger.kernel.org +Signed-off-by: Geliang Tang +Tested-by: Xuanqiang Luo +Reviewed-by: Matthieu Baerts (NGI0) +Signed-off-by: Matthieu Baerts (NGI0) +Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-2-6fb595bc86ef@kernel.org +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + net/mptcp/pm_userspace.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/net/mptcp/pm_userspace.c ++++ b/net/mptcp/pm_userspace.c +@@ -137,12 +137,15 @@ int mptcp_userspace_pm_get_local_id(stru + __be16 msk_sport = ((struct inet_sock *) + inet_sk((struct sock *)msk))->inet_sport; + struct mptcp_pm_addr_entry *entry; ++ int id; + + spin_lock_bh(&msk->pm.lock); + entry = mptcp_userspace_pm_lookup_addr(msk, &skc->addr); ++ id = entry ? entry->addr.id : -1; + spin_unlock_bh(&msk->pm.lock); +- if (entry) +- return entry->addr.id; ++ ++ if (id != -1) ++ return id; + + if (skc->addr.port == msk_sport) + skc->addr.port = 0; diff --git a/queue-6.12/series b/queue-6.12/series index 80d4091bb4..9a8bbf3aa2 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -289,3 +289,21 @@ mm-huge_memory-unlock-i_mmap_rwsem-before-releasing-.patch lib-alloc_tag-introduce-mem_alloc_profiling_permanen.patch mm-slab-prevent-unbounded-recursion-in-free-path-wit.patch gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch +usb-gadget-f_tcm-synchronize-delayed-set_alt-with-teardown.patch +usb-typec-ucsi-split-connector-lock-classes.patch +usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch +media-i2c-imx219-rename-vts-to-frm_length.patch +media-imx219-fix-maximum-frame-length-in-lines.patch +media-chips-media-wave5-support-cbp-profile.patch +media-uapi-rkisp-correct-name-version-enum.patch +wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch +wifi-ath6kl-fix-use-after-free-in-aggr_reset_state.patch +wifi-brcmfmac-fix-43752-sdio-fwvid-incorrectly-labelled-as-cypress-cyw.patch +wifi-brcmfmac-set-f2-blocksize-to-256-for-bcm43752.patch +alsa-hda-codecs-hdmi-disable-keep-alive-before-audio-format-change.patch +mptcp-pm-avoid-code-duplication-to-lookup-endp.patch +mptcp-add-mptcp_userspace_pm_lookup_addr-helper.patch +mptcp-pm-use-addr-entry-for-get_local_id.patch +mptcp-pm-userspace-fix-use-after-free-in-get_local_id.patch +kmemleak-iommu-iova-fix-transient-kmemleak-false-positive.patch +mm-kmemleak-fix-checksum-computation-for-per-cpu-objects.patch diff --git a/queue-6.12/usb-gadget-f_tcm-synchronize-delayed-set_alt-with-teardown.patch b/queue-6.12/usb-gadget-f_tcm-synchronize-delayed-set_alt-with-teardown.patch new file mode 100644 index 0000000000..26505a8eee --- /dev/null +++ b/queue-6.12/usb-gadget-f_tcm-synchronize-delayed-set_alt-with-teardown.patch @@ -0,0 +1,388 @@ +From sashal@kernel.org Thu Jul 30 21:29:51 2026 +From: Sasha Levin +Date: Thu, 30 Jul 2026 15:29:48 -0400 +Subject: usb: gadget: f_tcm: synchronize delayed set_alt with teardown +To: stable@vger.kernel.org +Cc: Cen Zhang , stable , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260730192948.3124448-1-sashal@kernel.org> + +From: Cen Zhang + +[ Upstream commit 79e2d75725c85607f8a9d87ae9cace62a19f767d ] + +The f_tcm set_alt() path defers endpoint setup to a work item and +completes the delayed status response from process context. The delayed +work uses f_tcm private state and may complete the setup request after +disconnect or function teardown has already moved on. + +Cancel and drain the delayed set_alt work when the function is unbound or +freed. For disable paths, which are reached under the composite device +lock, use a small state machine and a non-sleeping cancellation path +instead of cancel_work_sync(). If the work is already running, mark it +cancelled and let the worker own the cleanup; otherwise tcm_disable() can +cancel the queued work and clean up immediately. + +Also serialize the final delayed-status completion with the cancellation +check while holding the composite device lock. This prevents a disconnect +from clearing delayed_status while the worker is about to complete the +control request. + +Validation reproduced this kernel report: +BUG: KASAN: slab-use-after-free in tcm_delayed_set_alt+0x6c/0xef0 + +Call Trace: + + dump_stack_lvl+0x66/0xa0 + print_report+0xce/0x630 + ? tcm_delayed_set_alt+0x6c/0xef0 + ? srso_alias_return_thunk+0x5/0xfbef5 + ? __virt_addr_valid+0x188/0x320 + ? tcm_delayed_set_alt+0x6c/0xef0 + kasan_report+0xe0/0x110 + ? tcm_delayed_set_alt+0x6c/0xef0 + tcm_delayed_set_alt+0x6c/0xef0 + ? __pfx_tcm_delayed_set_alt+0x10/0x10 + ? process_one_work+0x4cb/0xb90 + ? rcu_is_watching+0x20/0x50 + ? tcm_delayed_set_alt+0x9/0xef0 + process_one_work+0x4d7/0xb90 + ? __pfx_process_one_work+0x10/0x10 + ? srso_alias_return_thunk+0x5/0xfbef5 + ? __list_add_valid_or_report+0x37/0xf0 + ? __pfx_tcm_delayed_set_alt+0x10/0x10 + ? srso_alias_return_thunk+0x5/0xfbef5 + worker_thread+0x2d8/0x570 + ? __pfx_worker_thread+0x10/0x10 + kthread+0x1ad/0x1f0 + ? __pfx_kthread+0x10/0x10 + ret_from_fork+0x3c9/0x540 + ? __pfx_ret_from_fork+0x10/0x10 + ? srso_alias_return_thunk+0x5/0xfbef5 + ? __switch_to+0x2e9/0x730 + ? __pfx_kthread+0x10/0x10 + ret_from_fork_asm+0x1a/0x30 + + +Allocated by task 544: + kasan_save_stack+0x33/0x60 + kasan_save_track+0x14/0x30 + __kasan_kmalloc+0x8f/0xa0 + tcm_alloc+0x68/0x180 + usb_get_function+0x36/0x60 + config_usb_cfg_link+0x125/0x1b0 + configfs_symlink+0x322/0x890 + vfs_symlink+0xc2/0x270 + filename_symlinkat+0x295/0x2f0 + __x64_sys_symlinkat+0x62/0x90 + do_syscall_64+0x115/0x6a0 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + +Freed by task 661: + kasan_save_stack+0x33/0x60 + kasan_save_track+0x14/0x30 + kasan_save_free_info+0x3b/0x60 + __kasan_slab_free+0x43/0x70 + kfree+0x2f9/0x530 + config_usb_cfg_unlink+0x173/0x1e0 + configfs_unlink+0x1fa/0x340 + vfs_unlink+0x15c/0x510 + filename_unlinkat+0x2ba/0x450 + __x64_sys_unlinkat+0x63/0x90 + do_syscall_64+0x115/0x6a0 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + +Fixes: c52661d60f63 ("usb-gadget: Initial merge of target module for UASP + BOT") +Cc: stable +Assisted-by: Codex:gpt-5.5 +Signed-off-by: Cen Zhang +Link: https://patch.msgid.link/20260627104153.3822495-1-zzzccc427@gmail.com +Signed-off-by: Greg Kroah-Hartman +[ adjusted context for 6.12's scalar `struct usbg_cdb cmd` and missing `stream_hash`, dropping the `hash_init()` context line ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/gadget/function/f_tcm.c | 192 ++++++++++++++++++++++++++++++------ + drivers/usb/gadget/function/tcm.h | 13 ++ + 2 files changed, 177 insertions(+), 28 deletions(-) + +--- a/drivers/usb/gadget/function/f_tcm.c ++++ b/drivers/usb/gadget/function/f_tcm.c +@@ -2014,31 +2014,158 @@ ep_fail: + return -ENOTSUPP; + } + +-struct guas_setup_wq { +- struct work_struct work; +- struct f_uas *fu; +- unsigned int alt; +-}; ++static void tcm_cleanup_old_alt(struct f_uas *fu) ++{ ++ if (fu->flags & USBG_IS_UAS) ++ uasp_cleanup_old_alt(fu); ++ else if (fu->flags & USBG_IS_BOT) ++ bot_cleanup_old_alt(fu); ++ fu->flags = 0; ++} ++ ++static void tcm_delayed_set_alt_done(struct f_uas *fu) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); ++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; ++ fu->delayed_set_alt_cancel = false; ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); ++} ++ ++static bool tcm_delayed_set_alt_cancelled(struct f_uas *fu) ++{ ++ bool cancelled; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); ++ cancelled = fu->delayed_set_alt_cancel; ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); ++ ++ return cancelled; ++} ++ ++static bool tcm_complete_delayed_status(struct f_uas *fu) ++{ ++ struct usb_composite_dev *cdev = fu->function.config->cdev; ++ struct usb_request *req = cdev->req; ++ unsigned long cdev_flags; ++ bool cancelled; ++ int ret; ++ ++ spin_lock_irqsave(&cdev->lock, cdev_flags); ++ spin_lock(&fu->delayed_set_alt_lock); ++ cancelled = fu->delayed_set_alt_cancel; ++ if (!cancelled) { ++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; ++ fu->delayed_set_alt_cancel = false; ++ } ++ spin_unlock(&fu->delayed_set_alt_lock); ++ ++ if (cancelled) { ++ spin_unlock_irqrestore(&cdev->lock, cdev_flags); ++ return false; ++ } ++ ++ if (cdev->delayed_status == 0) { ++ WARN(cdev, "%s: Unexpected call\n", __func__); ++ } else if (--cdev->delayed_status == 0) { ++ req->length = 0; ++ req->context = cdev; ++ ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); ++ if (ret == 0) { ++ cdev->setup_pending = true; ++ } else { ++ req->status = 0; ++ req->complete(cdev->gadget->ep0, req); ++ } ++ } ++ ++ spin_unlock_irqrestore(&cdev->lock, cdev_flags); ++ ++ return true; ++} ++ ++static bool tcm_cancel_delayed_set_alt(struct f_uas *fu) ++{ ++ bool cleanup = false; ++ bool cancel = false; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); ++ switch (fu->delayed_set_alt_state) { ++ case USBG_DELAYED_SET_ALT_IDLE: ++ cleanup = true; ++ break; ++ case USBG_DELAYED_SET_ALT_QUEUED: ++ case USBG_DELAYED_SET_ALT_RUNNING: ++ fu->delayed_set_alt_cancel = true; ++ cancel = true; ++ break; ++ } ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); ++ ++ if (cancel && cancel_work(&fu->delayed_set_alt)) { ++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); ++ if (fu->delayed_set_alt_state == USBG_DELAYED_SET_ALT_QUEUED) { ++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; ++ fu->delayed_set_alt_cancel = false; ++ cleanup = true; ++ } ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); ++ } ++ ++ return cleanup; ++} ++ ++static void tcm_cancel_delayed_set_alt_sync(struct f_uas *fu) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); ++ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) ++ fu->delayed_set_alt_cancel = true; ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); ++ ++ cancel_work_sync(&fu->delayed_set_alt); ++ ++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); ++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_IDLE; ++ fu->delayed_set_alt_cancel = false; ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); ++} + + static void tcm_delayed_set_alt(struct work_struct *wq) + { +- struct guas_setup_wq *work = container_of(wq, struct guas_setup_wq, +- work); +- struct f_uas *fu = work->fu; +- int alt = work->alt; ++ struct f_uas *fu = container_of(wq, struct f_uas, delayed_set_alt); ++ unsigned long flags; ++ unsigned int alt; ++ ++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); ++ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_QUEUED) { ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); ++ return; ++ } ++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_RUNNING; ++ alt = fu->delayed_alt; ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + +- kfree(work); ++ tcm_cleanup_old_alt(fu); + +- if (fu->flags & USBG_IS_BOT) +- bot_cleanup_old_alt(fu); +- if (fu->flags & USBG_IS_UAS) +- uasp_cleanup_old_alt(fu); ++ if (tcm_delayed_set_alt_cancelled(fu)) ++ goto out_done; + + if (alt == USB_G_ALT_INT_BBB) + bot_set_alt(fu); + else if (alt == USB_G_ALT_INT_UAS) + uasp_set_alt(fu); +- usb_composite_setup_continue(fu->function.config->cdev); ++ ++ if (tcm_complete_delayed_status(fu)) ++ return; ++ ++ tcm_cleanup_old_alt(fu); ++out_done: ++ tcm_delayed_set_alt_done(fu); + } + + static int tcm_get_alt(struct usb_function *f, unsigned intf) +@@ -2064,15 +2191,20 @@ static int tcm_set_alt(struct usb_functi + return -EOPNOTSUPP; + + if ((alt == USB_G_ALT_INT_BBB) || (alt == USB_G_ALT_INT_UAS)) { +- struct guas_setup_wq *work; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&fu->delayed_set_alt_lock, flags); ++ if (fu->delayed_set_alt_state != USBG_DELAYED_SET_ALT_IDLE) { ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, ++ flags); ++ return -EBUSY; ++ } ++ fu->delayed_alt = alt; ++ fu->delayed_set_alt_cancel = false; ++ fu->delayed_set_alt_state = USBG_DELAYED_SET_ALT_QUEUED; ++ spin_unlock_irqrestore(&fu->delayed_set_alt_lock, flags); + +- work = kmalloc(sizeof(*work), GFP_ATOMIC); +- if (!work) +- return -ENOMEM; +- INIT_WORK(&work->work, tcm_delayed_set_alt); +- work->fu = fu; +- work->alt = alt; +- schedule_work(&work->work); ++ schedule_work(&fu->delayed_set_alt); + return USB_GADGET_DELAYED_STATUS; + } + return -EOPNOTSUPP; +@@ -2082,11 +2214,8 @@ static void tcm_disable(struct usb_funct + { + struct f_uas *fu = to_f_uas(f); + +- if (fu->flags & USBG_IS_UAS) +- uasp_cleanup_old_alt(fu); +- else if (fu->flags & USBG_IS_BOT) +- bot_cleanup_old_alt(fu); +- fu->flags = 0; ++ if (tcm_cancel_delayed_set_alt(fu)) ++ tcm_cleanup_old_alt(fu); + } + + static int tcm_setup(struct usb_function *f, +@@ -2234,11 +2363,16 @@ static void tcm_free(struct usb_function + { + struct f_uas *tcm = to_f_uas(f); + ++ tcm_cancel_delayed_set_alt_sync(tcm); + kfree(tcm); + } + + static void tcm_unbind(struct usb_configuration *c, struct usb_function *f) + { ++ struct f_uas *fu = to_f_uas(f); ++ ++ tcm_cancel_delayed_set_alt_sync(fu); ++ tcm_cleanup_old_alt(fu); + usb_free_all_descriptors(f); + } + +@@ -2271,6 +2405,8 @@ static struct usb_function *tcm_alloc(st + fu->function.disable = tcm_disable; + fu->function.free_func = tcm_free; + fu->tpg = tpg_instances[i].tpg; ++ INIT_WORK(&fu->delayed_set_alt, tcm_delayed_set_alt); ++ spin_lock_init(&fu->delayed_set_alt_lock); + mutex_unlock(&tpg_instances_lock); + + return &fu->function; +--- a/drivers/usb/gadget/function/tcm.h ++++ b/drivers/usb/gadget/function/tcm.h +@@ -3,6 +3,7 @@ + #define __TARGET_USB_GADGET_H__ + + #include ++#include + /* #include */ + #include + #include +@@ -26,6 +27,12 @@ enum { + + #define USB_G_DEFAULT_SESSION_TAGS 128 + ++enum { ++ USBG_DELAYED_SET_ALT_IDLE = 0, ++ USBG_DELAYED_SET_ALT_QUEUED, ++ USBG_DELAYED_SET_ALT_RUNNING, ++}; ++ + struct tcm_usbg_nexus { + struct se_session *tvn_se_sess; + }; +@@ -117,6 +124,12 @@ struct f_uas { + #define USBG_IS_BOT (1 << 3) + #define USBG_BOT_CMD_PEND (1 << 4) + ++ struct work_struct delayed_set_alt; ++ spinlock_t delayed_set_alt_lock; /* protects delayed_set_alt_* */ ++ unsigned int delayed_alt; ++ unsigned int delayed_set_alt_state; ++ bool delayed_set_alt_cancel; ++ + struct usbg_cdb cmd; + struct usb_ep *ep_in; + struct usb_ep *ep_out; diff --git a/queue-6.12/usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch b/queue-6.12/usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch new file mode 100644 index 0000000000..58546c1ce1 --- /dev/null +++ b/queue-6.12/usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch @@ -0,0 +1,173 @@ +From sashal@kernel.org Thu Jul 30 21:39:03 2026 +From: Sasha Levin +Date: Thu, 30 Jul 2026 15:38:59 -0400 +Subject: usb: typec: ucsi: Fix race condition and ordering in port unregistration +To: stable@vger.kernel.org +Cc: Andrei Kuchynski , stable , Benson Leung , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260730193859.3130212-2-sashal@kernel.org> + +From: Andrei Kuchynski + +[ Upstream commit 7aa7d4bf9d3fa9a6a47b640ad103ab433b7ff261 ] + +A synchronization issue exists during port unregistration where pending +partner work items can race against workqueue destruction, leading to +use-after-free conditions: + + cros_ec_ucsi cros_ec_ucsi.3.auto: error -ETIMEDOUT: PPM init failed + BUG: kernel NULL pointer dereference, address: 0000000000000000 + RIP: 0010:__queue_work+0x83/0x4a0 + Call Trace: + + __cfi_delayed_work_timer_fn+0x10/0x10 + run_timer_softirq+0x3b6/0xbd0 + sched_clock_cpu+0xc/0x110 + irq_exit_rcu+0x18d/0x330 + fred_sysvec_apic_timer_interrupt+0x5e/0x80 + +Fix this by ensuring strict ordering and proper serialization during +teardown: + +1. Move ucsi_unregister_partner() to the beginning of the teardown +sequence and protect it under the connector mutex lock. +2. Ensure all pending partner tasks are explicitly flushed and finished +before the workqueue is destroyed. +3. Switch from mod_delayed_work() to a cancel_delayed_work() and +queue_delayed_work() sequence. This guarantees that items currently marked +as pending won't be scheduled an additional time, preventing a double +release of resources which leads to the following crash: + + Oops: general protection fault, probably for non-canonical address + 0xdead000000000122: 0000 [#1] SMP NOPTI + Workqueue: cros_ec_ucsi.3.auto-con2 ucsi_poll_worker + RIP: 0010:ucsi_poll_worker+0x65/0x1e0 + Call Trace: + + process_scheduled_works+0x218/0x6d0 + worker_thread+0x188/0x3f0 + __cfi_worker_thread+0x10/0x10 + kthread+0x226/0x2a0 + +To ensure these rules are applied identically across both the normal +teardown and the ucsi_init() error paths, consolidate the cleanup logic +into a new helper, ucsi_unregister_port(). + +Cc: stable +Fixes: b9aa02ca39a4 ("usb: typec: ucsi: Add polling mechanism for partner tasks like alt mode checking") +Fixes: b13abcb7ddd8 ("usb: typec: ucsi: Fix NULL pointer access") +Fixes: fac4b8633fd6 ("usb: ucsi: Ensure connector delayed work items are flushed") +Signed-off-by: Andrei Kuchynski +Reviewed-by: Benson Leung +Link: https://patch.msgid.link/20260707141736.1635698-1-akuchynski@chromium.org +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/typec/ucsi/ucsi.c | 82 +++++++++++++++++++----------------------- + 1 file changed, 39 insertions(+), 43 deletions(-) + +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -1719,6 +1719,42 @@ out_unlock: + return ret; + } + ++static void ucsi_unregister_port(struct ucsi_connector *con) ++{ ++ struct ucsi_work *uwork; ++ ++ if (con->wq) { ++ mutex_lock(&con->lock); ++ ucsi_unregister_partner(con); ++ /* ++ * queue delayed items immediately so they can execute ++ * and free themselves before the wq is destroyed ++ */ ++ list_for_each_entry(uwork, &con->partner_tasks, node) { ++ if (cancel_delayed_work(&uwork->work)) ++ queue_delayed_work(con->wq, &uwork->work, 0); ++ } ++ mutex_unlock(&con->lock); ++ ++ destroy_workqueue(con->wq); ++ con->wq = NULL; ++ } else { ++ ucsi_unregister_partner(con); ++ } ++ ++ ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); ++ ucsi_unregister_port_psy(con); ++ ++ usb_power_delivery_unregister_capabilities(con->port_sink_caps); ++ con->port_sink_caps = NULL; ++ usb_power_delivery_unregister_capabilities(con->port_source_caps); ++ con->port_source_caps = NULL; ++ usb_power_delivery_unregister(con->pd); ++ con->pd = NULL; ++ typec_unregister_port(con->port); ++ con->port = NULL; ++} ++ + static u64 ucsi_get_supported_notifications(struct ucsi *ucsi) + { + u16 features = ucsi->cap.features; +@@ -1844,22 +1880,8 @@ err_unregister: + for (i = 0; i < ucsi->cap.num_connectors; i++) + lockdep_unregister_key(&connector[i].lock_key); + +- for (con = connector; con->port; con++) { +- if (con->wq) +- destroy_workqueue(con->wq); +- ucsi_unregister_partner(con); +- ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON); +- ucsi_unregister_port_psy(con); +- +- usb_power_delivery_unregister_capabilities(con->port_sink_caps); +- con->port_sink_caps = NULL; +- usb_power_delivery_unregister_capabilities(con->port_source_caps); +- con->port_source_caps = NULL; +- usb_power_delivery_unregister(con->pd); +- con->pd = NULL; +- typec_unregister_port(con->port); +- con->port = NULL; +- } ++ for (con = connector; con->port; con++) ++ ucsi_unregister_port(con); + kfree(connector); + err_reset: + memset(&ucsi->cap, 0, sizeof(ucsi->cap)); +@@ -2087,33 +2109,7 @@ void ucsi_unregister(struct ucsi *ucsi) + + for (i = 0; i < ucsi->cap.num_connectors; i++) { + cancel_work_sync(&ucsi->connector[i].work); +- +- if (ucsi->connector[i].wq) { +- struct ucsi_work *uwork; +- +- mutex_lock(&ucsi->connector[i].lock); +- /* +- * queue delayed items immediately so they can execute +- * and free themselves before the wq is destroyed +- */ +- list_for_each_entry(uwork, &ucsi->connector[i].partner_tasks, node) +- mod_delayed_work(ucsi->connector[i].wq, &uwork->work, 0); +- mutex_unlock(&ucsi->connector[i].lock); +- destroy_workqueue(ucsi->connector[i].wq); +- } +- +- ucsi_unregister_partner(&ucsi->connector[i]); +- ucsi_unregister_altmodes(&ucsi->connector[i], +- UCSI_RECIPIENT_CON); +- ucsi_unregister_port_psy(&ucsi->connector[i]); +- +- usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_sink_caps); +- ucsi->connector[i].port_sink_caps = NULL; +- usb_power_delivery_unregister_capabilities(ucsi->connector[i].port_source_caps); +- ucsi->connector[i].port_source_caps = NULL; +- usb_power_delivery_unregister(ucsi->connector[i].pd); +- ucsi->connector[i].pd = NULL; +- typec_unregister_port(ucsi->connector[i].port); ++ ucsi_unregister_port(&ucsi->connector[i]); + lockdep_unregister_key(&ucsi->connector[i].lock_key); + } + diff --git a/queue-6.12/usb-typec-ucsi-split-connector-lock-classes.patch b/queue-6.12/usb-typec-ucsi-split-connector-lock-classes.patch new file mode 100644 index 0000000000..7f3ba7f383 --- /dev/null +++ b/queue-6.12/usb-typec-ucsi-split-connector-lock-classes.patch @@ -0,0 +1,124 @@ +From sashal@kernel.org Thu Jul 30 21:39:02 2026 +From: Sasha Levin +Date: Thu, 30 Jul 2026 15:38:58 -0400 +Subject: usb: typec: ucsi: split connector lock classes +To: stable@vger.kernel.org +Cc: Sergey Senozhatsky , Heikki Krogerus , Greg Kroah-Hartman , Sasha Levin +Message-ID: <20260730193859.3130212-1-sashal@kernel.org> + +From: Sergey Senozhatsky + +[ Upstream commit 8c22256bbafad3dc5fdbe9f684d045b67ff06a68 ] + +Lockdep detects a possible recursive locking scenario during +ucsi init: + +[ 5.418616] ============================================ +[ 5.418634] WARNING: possible recursive locking detected +[ 5.418706] -------------------------------------------- +[ 5.418725] kworker/4:1/82 is trying to acquire lock: +[ 5.418759] ffff888119a34648 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi] +[ 5.418801] + but task is already holding lock: +[ 5.418835] ffff888119a34080 (&con->lock){+.+.}-{3:3}, at: ucsi_init_work+0x1a78/0x2eb0 [typec_ucsi] +[ 5.418884] + other info that might help us debug this: +[ 5.418904] Possible unsafe locking scenario: + +[ 5.418937] CPU0 +[ 5.418956] ---- +[ 5.418991] lock(&con->lock); +[ 5.419013] lock(&con->lock); +[ 5.419033] + *** DEADLOCK *** + +[ 5.419387] Call Trace: +[ 5.419406] +[ 5.419425] dump_stack_lvl+0x61/0xa0 +[ 5.419448] print_deadlock_bug+0x4a6/0x650 +[ 5.419483] __lock_acquire+0x62b6/0x7f50 +[ 5.419507] lock_acquire+0x11b/0x390 +[ 5.419654] __mutex_lock+0xbc/0xcd0 +[ 5.419741] ucsi_init_work+0x1a78/0x2eb0 +[ 5.419785] ? worker_thread+0xf53/0x2bc0 +[ 5.419819] worker_thread+0xff4/0x2bc0 +[ 5.419842] kthread+0x2a7/0x330 +[ 5.419863] ? __pfx_worker_thread+0x10/0x10 +[ 5.419896] ? __pfx_kthread+0x10/0x10 +[ 5.419916] ret_from_fork+0x38/0x70 +[ 5.419936] ? __pfx_kthread+0x10/0x10 +[ 5.419969] ret_from_fork_asm+0x1b/0x30 +[ 5.419991] +[ 5.420009] ---[ end trace 0000000000000000 ]--- + +The problem is that all connector locks belong to the same +lockdep lock class, so the following loop: + + for (i = 0; i < ucsi->cap.num_connectors; i++) + ucsi_register_port(connector[i]) + mutex_lock(&connector[i]->lock) + +looks like a recursive acquire of the same mutex. Put each connector +lock into a dedicated lock class so that lockdep doesn't see it as a +possible recursion. + +Signed-off-by: Sergey Senozhatsky +Reviewed-by: Heikki Krogerus +Link: https://patch.msgid.link/20260515060042.136083-1-senozhatsky@chromium.org +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/typec/ucsi/ucsi.c | 8 ++++++++ + drivers/usb/typec/ucsi/ucsi.h | 1 + + 2 files changed, 9 insertions(+) + +--- a/drivers/usb/typec/ucsi/ucsi.c ++++ b/drivers/usb/typec/ucsi/ucsi.c +@@ -1569,6 +1569,7 @@ static int ucsi_register_port(struct ucs + INIT_WORK(&con->work, ucsi_handle_connector_change); + init_completion(&con->complete); + mutex_init(&con->lock); ++ lockdep_set_class(&con->lock, &con->lock_key); + INIT_LIST_HEAD(&con->partner_tasks); + con->ucsi = ucsi; + +@@ -1808,6 +1809,9 @@ static int ucsi_init(struct ucsi *ucsi) + goto err_reset; + } + ++ for (i = 0; i < ucsi->cap.num_connectors; i++) ++ lockdep_register_key(&connector[i].lock_key); ++ + /* Register all connectors */ + for (i = 0; i < ucsi->cap.num_connectors; i++) { + connector[i].num = i + 1; +@@ -1837,6 +1841,9 @@ static int ucsi_init(struct ucsi *ucsi) + return 0; + + err_unregister: ++ for (i = 0; i < ucsi->cap.num_connectors; i++) ++ lockdep_unregister_key(&connector[i].lock_key); ++ + for (con = connector; con->port; con++) { + if (con->wq) + destroy_workqueue(con->wq); +@@ -2107,6 +2114,7 @@ void ucsi_unregister(struct ucsi *ucsi) + usb_power_delivery_unregister(ucsi->connector[i].pd); + ucsi->connector[i].pd = NULL; + typec_unregister_port(ucsi->connector[i].port); ++ lockdep_unregister_key(&ucsi->connector[i].lock_key); + } + + kfree(ucsi->connector); +--- a/drivers/usb/typec/ucsi/ucsi.h ++++ b/drivers/usb/typec/ucsi/ucsi.h +@@ -422,6 +422,7 @@ struct ucsi_connector { + + struct ucsi *ucsi; + struct mutex lock; /* port lock */ ++ struct lock_class_key lock_key; + struct work_struct work; + struct completion complete; + struct workqueue_struct *wq; diff --git a/queue-6.12/wifi-ath6kl-fix-use-after-free-in-aggr_reset_state.patch b/queue-6.12/wifi-ath6kl-fix-use-after-free-in-aggr_reset_state.patch new file mode 100644 index 0000000000..00eb3b1c1f --- /dev/null +++ b/queue-6.12/wifi-ath6kl-fix-use-after-free-in-aggr_reset_state.patch @@ -0,0 +1,50 @@ +From stable+bounces-296831-greg=kroah.com@vger.kernel.org Thu Aug 6 15:51:53 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:44:31 -0400 +Subject: wifi: ath6kl: fix use-after-free in aggr_reset_state() +To: stable@vger.kernel.org +Cc: Daniel Hodges , Vasanthakumar Thiagarajan , Jeff Johnson , Sasha Levin +Message-ID: <20260806134431.555434-1-sashal@kernel.org> + +From: Daniel Hodges + +[ Upstream commit ba7debb4dd6427386862220e8335a53a4bfc235d ] + +The aggr_reset_state() function uses timer_delete() (non-synchronous) +for the aggregation timer before proceeding to delete TID state and +before the structure is freed by callers like aggr_module_destroy(). + +If the timer callback (aggr_timeout) is executing when aggr_reset_state() +is called, the callback will continue to access aggr_conn fields like +rx_tid[] and stat[] which may be freed immediately after by +kfree(aggr_info->aggr_conn) in aggr_module_destroy(). + +Additionally, the timer callback can re-arm itself via mod_timer() while +aggr_reset_state() is running, creating a more complex race condition. + +Use timer_delete_sync() instead to ensure any running timer callback +has completed before returning. + +Fixes: bdcd81707973 ("Add ath6kl cleaned up driver") +Cc: stable@vger.kernel.org +Signed-off-by: Daniel Hodges +Reviewed-by: Vasanthakumar Thiagarajan +Link: https://patch.msgid.link/20260206185207.30098-1-git@danielhodges.dev +Signed-off-by: Jeff Johnson +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath6kl/txrx.c ++++ b/drivers/net/wireless/ath/ath6kl/txrx.c +@@ -1829,7 +1829,7 @@ void aggr_reset_state(struct aggr_info_c + return; + + if (aggr_conn->timer_scheduled) { +- del_timer(&aggr_conn->timer); ++ timer_delete_sync(&aggr_conn->timer); + aggr_conn->timer_scheduled = false; + } + diff --git a/queue-6.12/wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch b/queue-6.12/wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch new file mode 100644 index 0000000000..99256475df --- /dev/null +++ b/queue-6.12/wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch @@ -0,0 +1,292 @@ +From stable+bounces-296820-greg=kroah.com@vger.kernel.org Thu Aug 6 15:32:56 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:22:30 -0400 +Subject: wifi: brcmfmac: drain bus_reset work on device removal +To: stable@vger.kernel.org +Cc: Fan Wu , Arend van Spriel , Johannes Berg , Sasha Levin +Message-ID: <20260806132230.416656-1-sashal@kernel.org> + +From: Fan Wu + +[ Upstream commit 43b25879f004c98defa2776bedc6ca4763c51945 ] + +brcmf_fw_crashed() and the debugfs "reset" entry both schedule +drvr->bus_reset, whose callback recovers drvr through container_of() +and dereferences it. The removal path frees drvr (brcmf_free -> +wiphy_free) without draining the work, so a bus_reset callback pending +or running during removal can outlive drvr. + +Cancellation cannot live in brcmf_detach() or brcmf_free(): the work +callback reaches teardown through the bus .reset op (PCIe +brcmf_pcie_reset -> brcmf_detach; SDIO brcmf_sdio_bus_reset -> +brcmf_sdiod_remove -> brcmf_free), so cancelling there would wait for +the running work and deadlock. + +Add a per-bus mutex (bus_reset_lock) and route all arming through +brcmf_bus_schedule_reset(), which under the lock skips when the bus is +marked removing. Each bus remove entry calls +brcmf_bus_cancel_reset_work(), which under the same lock sets removing +and cancels the work. Holding the mutex across cancel_work_sync() makes +the set-removing + drain step atomic. Every producer reaches the arming +path from process context -- the PCIe firmware-halt notification runs in +the threaded IRQ handler (brcmf_pcie_isr_thread) and the SDIO hostmail +path runs from the data workqueue -- so the mutex is taken only in +sleepable contexts. Where applicable the remove entry first stops the +firmware-crash producer: on PCIe mask the mailbox and synchronize_irq; +on SDIO unregister the bus interrupt and cancel the data worker, which +also reports firmware halts through brcmf_fw_crashed(). The mutex is +initialized at bus allocation. The SDIO suspend power-off path frees +drvr through the same brcmf_sdiod_remove() and takes the same lock; +resume re-allows the work only on a successful re-probe. + +Also guard brcmf_fw_crashed() against a NULL bus_if/drvr: it can fire +before brcmf_attach() wires up drvr, and it dereferences drvr +(bphy_err/brcmf_dev_coredump) before reaching the arming gate. + +The bus_reset work is shared across buses, so the drain is applied to +every remove path: PCIe (the .reset op introduced by the Fixes commit), +SDIO (arms the same work through brcmf_fw_crashed()), and USB (via the +debugfs "reset" entry). cancel_work_sync() drains a running or pending +bus_reset work item before removal frees drvr, and patch 1/2 makes the +scratch-buffer release safe when reset teardown has already released +those DMA buffers. + +This patch fixes the lifetime of the bus_reset work item itself. It does +not attempt to address the separate, pre-existing lifetime of the +asynchronous firmware completion started by the PCIe reset path. That +callback needs its own lifetime/ownership protocol and is being tracked +separately. + +This issue was found by an in-house static analysis tool. + +Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash") +Cc: stable@vger.kernel.org +Signed-off-by: Fan Wu +Assisted-by: Codex:gpt-5.6 +Acked-by: Arend van Spriel +Link: https://patch.msgid.link/20260718024353.3147201-3-fanwu01@zju.edu.cn +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 13 +++ + drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h | 6 + + drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 46 ++++++++++++-- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 6 + + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 6 + + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h | 1 + drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c | 3 + 7 files changed, 77 insertions(+), 4 deletions(-) + +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +@@ -1064,6 +1064,7 @@ static int brcmf_ops_sdio_probe(struct s + bus_if = kzalloc(sizeof(*bus_if), GFP_KERNEL); + if (!bus_if) + return -ENOMEM; ++ mutex_init(&bus_if->bus_reset_lock); + sdiodev = kzalloc(sizeof(*sdiodev), GFP_KERNEL); + if (!sdiodev) { + kfree(bus_if); +@@ -1125,6 +1126,14 @@ static void brcmf_ops_sdio_remove(struct + if (func->num != 1) + return; + ++ /* Drain bus_reset before the shared brcmf_sdiod_remove() ++ * teardown, which the SDIO reset callback also reaches. The ++ * data worker can arm bus_reset via brcmf_fw_crashed(); cancel ++ * it first. ++ */ ++ brcmf_sdio_cancel_datawork(sdiodev->bus); ++ brcmf_bus_cancel_reset_work(bus_if); ++ + /* only proceed with rest of cleanup if func 1 */ + brcmf_sdiod_remove(sdiodev); + +@@ -1199,6 +1208,8 @@ static int brcmf_ops_sdio_suspend(struct + } else { + /* power will be cut so remove device, probe again in resume */ + brcmf_sdiod_intr_unregister(sdiodev); ++ brcmf_sdio_cancel_datawork(sdiodev->bus); ++ brcmf_bus_cancel_reset_work(bus_if); + ret = brcmf_sdiod_remove(sdiodev); + if (ret) + brcmf_err("Failed to remove device on suspend\n"); +@@ -1224,6 +1235,8 @@ static int brcmf_ops_sdio_resume(struct + ret = brcmf_sdiod_probe(sdiodev); + if (ret) + brcmf_err("Failed to probe device on resume\n"); ++ else ++ brcmf_bus_allow_reset_work(bus_if); + } else { + if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported) + disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include "debug.h" + + /* IDs of the 6 default common rings of msgbuf protocol */ +@@ -179,6 +180,8 @@ struct brcmf_bus { + enum brcmf_fwvendor fwvid; + bool always_use_fws_queue; + bool wowl_supported; ++ bool removing; /* device removal in progress; quiesce async work */ ++ struct mutex bus_reset_lock; + + const struct brcmf_bus_ops *ops; + struct brcmf_bus_msgbuf *msgbuf; +@@ -186,6 +189,9 @@ struct brcmf_bus { + struct list_head list; + }; + ++void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if); ++void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if); ++ + /* + * callback wrappers + */ +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +@@ -1162,6 +1162,35 @@ static int brcmf_revinfo_read(struct seq + return 0; + } + ++/* ++ * Serialize arming from debugfs reset and brcmf_fw_crashed() against ++ * teardown. The remove path sets ->removing and drains the work while ++ * holding bus_reset_lock, so a racing armer is either drained or skips it. ++ */ ++static void brcmf_bus_schedule_reset(struct brcmf_bus *bus_if) ++{ ++ mutex_lock(&bus_if->bus_reset_lock); ++ if (bus_if->drvr && bus_if->drvr->bus_reset.func && !bus_if->removing) ++ schedule_work(&bus_if->drvr->bus_reset); ++ mutex_unlock(&bus_if->bus_reset_lock); ++} ++ ++void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if) ++{ ++ mutex_lock(&bus_if->bus_reset_lock); ++ bus_if->removing = true; ++ if (bus_if->drvr) ++ cancel_work_sync(&bus_if->drvr->bus_reset); ++ mutex_unlock(&bus_if->bus_reset_lock); ++} ++ ++void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if) ++{ ++ mutex_lock(&bus_if->bus_reset_lock); ++ bus_if->removing = false; ++ mutex_unlock(&bus_if->bus_reset_lock); ++} ++ + static void brcmf_core_bus_reset(struct work_struct *work) + { + struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, +@@ -1182,7 +1211,7 @@ static ssize_t bus_reset_write(struct fi + if (value != 1) + return -EINVAL; + +- schedule_work(&drvr->bus_reset); ++ brcmf_bus_schedule_reset(drvr->bus_if); + + return count; + } +@@ -1410,14 +1439,23 @@ void brcmf_dev_coredump(struct device *d + void brcmf_fw_crashed(struct device *dev) + { + struct brcmf_bus *bus_if = dev_get_drvdata(dev); +- struct brcmf_pub *drvr = bus_if->drvr; ++ struct brcmf_pub *drvr; ++ ++ /* May fire before brcmf_attach() wires up drvr, or after removal ++ * has cleared it; guard the derefs below (and the arming gate in ++ * brcmf_bus_schedule_reset() already checks drvr/->removing). ++ */ ++ if (!bus_if) ++ return; ++ drvr = bus_if->drvr; ++ if (!drvr) ++ return; + + bphy_err(drvr, "Firmware has halted or crashed\n"); + + brcmf_dev_coredump(dev); + +- if (drvr->bus_reset.func) +- schedule_work(&drvr->bus_reset); ++ brcmf_bus_schedule_reset(bus_if); + } + + void brcmf_detach(struct device *dev) +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +@@ -2462,6 +2462,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, c + ret = -ENOMEM; + goto fail; + } ++ mutex_init(&bus->bus_reset_lock); + bus->msgbuf = kzalloc(sizeof(*bus->msgbuf), GFP_KERNEL); + if (!bus->msgbuf) { + ret = -ENOMEM; +@@ -2547,6 +2548,11 @@ brcmf_pcie_remove(struct pci_dev *pdev) + if (devinfo->ci) + brcmf_pcie_intr_disable(devinfo); + ++ if (devinfo->irq_allocated) ++ synchronize_irq(pdev->irq); ++ ++ brcmf_bus_cancel_reset_work(bus); ++ + brcmf_detach(&pdev->dev); + brcmf_free(&pdev->dev); + +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +@@ -4550,6 +4550,12 @@ fail: + return NULL; + } + ++void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus) ++{ ++ if (bus) ++ cancel_work_sync(&bus->datawork); ++} ++ + /* Detach and free everything */ + void brcmf_sdio_remove(struct brcmf_sdio *bus) + { +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +@@ -361,6 +361,7 @@ int brcmf_sdiod_remove(struct brcmf_sdio + struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); + void brcmf_sdio_remove(struct brcmf_sdio *bus); + void brcmf_sdio_isr(struct brcmf_sdio *bus, bool in_isr); ++void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus); + + void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active); + void brcmf_sdio_wowl_config(struct device *dev, bool enabled); +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +@@ -1254,6 +1254,7 @@ static int brcmf_usb_probe_cb(struct brc + ret = -ENOMEM; + goto fail; + } ++ mutex_init(&bus->bus_reset_lock); + + bus->dev = dev; + bus_pub->bus = bus; +@@ -1320,6 +1321,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usb + return; + brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo); + ++ brcmf_bus_cancel_reset_work(devinfo->bus_pub.bus); ++ + brcmf_detach(devinfo->dev); + brcmf_free(devinfo->dev); + kfree(devinfo->bus_pub.bus); diff --git a/queue-6.12/wifi-brcmfmac-fix-43752-sdio-fwvid-incorrectly-labelled-as-cypress-cyw.patch b/queue-6.12/wifi-brcmfmac-fix-43752-sdio-fwvid-incorrectly-labelled-as-cypress-cyw.patch new file mode 100644 index 0000000000..f4ef305363 --- /dev/null +++ b/queue-6.12/wifi-brcmfmac-fix-43752-sdio-fwvid-incorrectly-labelled-as-cypress-cyw.patch @@ -0,0 +1,130 @@ +From stable+bounces-296832-greg=kroah.com@vger.kernel.org Thu Aug 6 15:50:30 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:49:36 -0400 +Subject: wifi: brcmfmac: fix 43752 SDIO FWVID incorrectly labelled as Cypress (CYW) +To: stable@vger.kernel.org +Cc: Gokul Sivakumar , Arend van Spriel , Johannes Berg , Sasha Levin +Message-ID: <20260806134937.599576-1-sashal@kernel.org> + +From: Gokul Sivakumar + +[ Upstream commit 74e2ef72bd4b25ce21c8f309d4f5b91b5df9ff5b ] + +Cypress(Infineon) is not the vendor for this 43752 SDIO WLAN chip, and so +has not officially released any firmware binary for it. It is incorrect to +maintain this WLAN chip with firmware vendor ID as "CYW". So relabel the +chip's firmware Vendor ID as "WCC" as suggested by the maintainer. + +Fixes: d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") +Fixes: f74f1ec22dc2 ("wifi: brcmfmac: add support for Cypress firmware api") +Signed-off-by: Gokul Sivakumar +Acked-by: Arend van Spriel +Link: https://patch.msgid.link/20250724101136.6691-1-gokulkumar.sivakumar@infineon.com +Signed-off-by: Johannes Berg +Stable-dep-of: 29ab31f3f271 ("wifi: brcmfmac: set F2 blocksize to 256 for BCM43752") +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 2 +- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c | 4 ++-- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 8 ++++---- + drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h | 2 +- + include/linux/mmc/sdio_ids.h | 2 +- + 5 files changed, 9 insertions(+), 9 deletions(-) + +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +@@ -991,9 +991,9 @@ static const struct sdio_device_id brcmf + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359, WCC), ++ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43752, WCC), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373, CYW), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012, CYW), +- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, CYW), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359, CYW), + CYW_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439, CYW), + { /* end: all zeroes */ } +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +@@ -738,7 +738,7 @@ static u32 brcmf_chip_tcm_rambase(struct + case BRCM_CC_4364_CHIP_ID: + case CY_CC_4373_CHIP_ID: + return 0x160000; +- case CY_CC_43752_CHIP_ID: ++ case BRCM_CC_43752_CHIP_ID: + case BRCM_CC_4377_CHIP_ID: + return 0x170000; + case BRCM_CC_4378_CHIP_ID: +@@ -1465,7 +1465,7 @@ bool brcmf_chip_sr_capable(struct brcmf_ + reg = chip->ops->read32(chip->ctx, addr); + return (reg & CC_SR_CTL0_ENABLE_MASK) != 0; + case BRCM_CC_4359_CHIP_ID: +- case CY_CC_43752_CHIP_ID: ++ case BRCM_CC_43752_CHIP_ID: + case CY_CC_43012_CHIP_ID: + addr = CORE_CC_REG(pmu->base, retention_ctl); + reg = chip->ops->read32(chip->ctx, addr); +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +@@ -654,10 +654,10 @@ static const struct brcmf_firmware_mappi + BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354), + BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), + BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), ++ BRCMF_FW_ENTRY(BRCM_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752), + BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373), + BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012), + BRCMF_FW_ENTRY(CY_CC_43439_CHIP_ID, 0xFFFFFFFF, 43439), +- BRCMF_FW_ENTRY(CY_CC_43752_CHIP_ID, 0xFFFFFFFF, 43752) + }; + + #define TXCTL_CREDITS 2 +@@ -3425,8 +3425,8 @@ err: + + static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus) + { +- if (bus->ci->chip == CY_CC_43012_CHIP_ID || +- bus->ci->chip == CY_CC_43752_CHIP_ID) ++ if (bus->ci->chip == BRCM_CC_43752_CHIP_ID || ++ bus->ci->chip == CY_CC_43012_CHIP_ID) + return true; + else + return false; +@@ -4274,8 +4274,8 @@ static void brcmf_sdio_firmware_callback + bus->hostintmask, NULL); + + switch (sdiod->func1->device) { ++ case SDIO_DEVICE_ID_BROADCOM_43752: + case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373: +- case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752: + brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n", + CY_4373_F2_WATERMARK); + brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, +--- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h ++++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +@@ -52,13 +52,13 @@ + #define BRCM_CC_43664_CHIP_ID 43664 + #define BRCM_CC_43666_CHIP_ID 43666 + #define BRCM_CC_4371_CHIP_ID 0x4371 ++#define BRCM_CC_43752_CHIP_ID 43752 + #define BRCM_CC_4377_CHIP_ID 0x4377 + #define BRCM_CC_4378_CHIP_ID 0x4378 + #define BRCM_CC_4387_CHIP_ID 0x4387 + #define CY_CC_4373_CHIP_ID 0x4373 + #define CY_CC_43012_CHIP_ID 43012 + #define CY_CC_43439_CHIP_ID 43439 +-#define CY_CC_43752_CHIP_ID 43752 + + /* USB Device IDs */ + #define BRCM_USB_43143_DEVICE_ID 0xbd1e +--- a/include/linux/mmc/sdio_ids.h ++++ b/include/linux/mmc/sdio_ids.h +@@ -76,7 +76,7 @@ + #define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6 + #define SDIO_DEVICE_ID_BROADCOM_43439 0xa9af + #define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf +-#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752 0xaae8 ++#define SDIO_DEVICE_ID_BROADCOM_43752 0xaae8 + + #define SDIO_VENDOR_ID_CYPRESS 0x04b4 + #define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439 0xbd3d diff --git a/queue-6.12/wifi-brcmfmac-set-f2-blocksize-to-256-for-bcm43752.patch b/queue-6.12/wifi-brcmfmac-set-f2-blocksize-to-256-for-bcm43752.patch new file mode 100644 index 0000000000..3a3476bca2 --- /dev/null +++ b/queue-6.12/wifi-brcmfmac-set-f2-blocksize-to-256-for-bcm43752.patch @@ -0,0 +1,59 @@ +From stable+bounces-296833-greg=kroah.com@vger.kernel.org Thu Aug 6 15:50:34 2026 +From: Sasha Levin +Date: Thu, 6 Aug 2026 09:49:37 -0400 +Subject: wifi: brcmfmac: set F2 blocksize to 256 for BCM43752 +To: stable@vger.kernel.org +Cc: LiangCheng Wang , Arend van Spriel , Johannes Berg , Sasha Levin +Message-ID: <20260806134937.599576-2-sashal@kernel.org> + +From: LiangCheng Wang + +[ Upstream commit 29ab31f3f27157648f2f7e6d5e1fd9792fdf0614 ] + +The BCM43752 is not reliable with the default 512-byte SDIO function 2 +block size: on an i.MX8MP board with an AMPAK AP6275S module at +SDR104 / 200 MHz, an iperf TX stress test kills WLAN within seconds: + + mmc_submit_one: CMD53 sg block write failed -84 + brcmf_sdio_dpc: failed backplane access over SDIO, halting operation + +Commit d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") +set up the 43752 like the 4373 for the F2 watermark but missed the F2 +block size, which the 4373 limits to 256 bytes. The vendor driver +(bcmdhd) also programs a 256-byte F2 block size for this chip and runs +the same hardware without errors. + +Group the 43752 with the 4373, matching the F2 watermark handling. +With this change a 10-minute bidirectional iperf3 soak completes with +zero SDIO errors at ~270 Mbit/s in each direction. + +Backporting note: kernels before v6.18 name this id +SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, so on those trees the case +label added by this patch must be adjusted to that name. Cherry-picking +the rename commit 74e2ef72bd4b ("wifi: brcmfmac: fix 43752 SDIO FWVID +incorrectly labelled as Cypress (CYW)") first is not a clean +alternative: on trees before v6.17 its context collides with the 43751 +additions, and trees before v6.2 lack the FWVID framework it touches. + +Fixes: d2587c57ffd8 ("brcmfmac: add 43752 SDIO ids and initialization") +Cc: stable@vger.kernel.org # see patch description, needs adjustments for <= 6.17 +Signed-off-by: LiangCheng Wang +Acked-by: Arend van Spriel +Link: https://patch.msgid.link/20260715-b43752-f2-blksz-v2-1-f9be49856050@gmail.com +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +@@ -906,6 +906,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_ + return ret; + } + switch (sdiodev->func2->device) { ++ case SDIO_DEVICE_ID_BROADCOM_43752: + case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373: + f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE; + break; -- 2.47.3