]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.12-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Aug 2026 12:23:52 +0000 (14:23 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 7 Aug 2026 12:23:52 +0000 (14:23 +0200)
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

20 files changed:
queue-6.12/alsa-hda-codecs-hdmi-disable-keep-alive-before-audio-format-change.patch [new file with mode: 0644]
queue-6.12/gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch
queue-6.12/kmemleak-iommu-iova-fix-transient-kmemleak-false-positive.patch [new file with mode: 0644]
queue-6.12/media-chips-media-wave5-support-cbp-profile.patch [new file with mode: 0644]
queue-6.12/media-i2c-imx219-rename-vts-to-frm_length.patch [new file with mode: 0644]
queue-6.12/media-imx219-fix-maximum-frame-length-in-lines.patch [new file with mode: 0644]
queue-6.12/media-uapi-rkisp-correct-name-version-enum.patch [new file with mode: 0644]
queue-6.12/mm-kmemleak-fix-checksum-computation-for-per-cpu-objects.patch [new file with mode: 0644]
queue-6.12/mptcp-add-mptcp_userspace_pm_lookup_addr-helper.patch [new file with mode: 0644]
queue-6.12/mptcp-pm-avoid-code-duplication-to-lookup-endp.patch [new file with mode: 0644]
queue-6.12/mptcp-pm-use-addr-entry-for-get_local_id.patch [new file with mode: 0644]
queue-6.12/mptcp-pm-userspace-fix-use-after-free-in-get_local_id.patch [new file with mode: 0644]
queue-6.12/series
queue-6.12/usb-gadget-f_tcm-synchronize-delayed-set_alt-with-teardown.patch [new file with mode: 0644]
queue-6.12/usb-typec-ucsi-fix-race-condition-and-ordering-in-port-unregistration.patch [new file with mode: 0644]
queue-6.12/usb-typec-ucsi-split-connector-lock-classes.patch [new file with mode: 0644]
queue-6.12/wifi-ath6kl-fix-use-after-free-in-aggr_reset_state.patch [new file with mode: 0644]
queue-6.12/wifi-brcmfmac-drain-bus_reset-work-on-device-removal.patch [new file with mode: 0644]
queue-6.12/wifi-brcmfmac-fix-43752-sdio-fwvid-incorrectly-labelled-as-cypress-cyw.patch [new file with mode: 0644]
queue-6.12/wifi-brcmfmac-set-f2-blocksize-to-256-for-bcm43752.patch [new file with mode: 0644]

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 (file)
index 0000000..39a319b
--- /dev/null
@@ -0,0 +1,130 @@
+From stable+bounces-296929-greg=kroah.com@vger.kernel.org Thu Aug  6 18:18:31 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <kai.vehmanen@linux.intel.com>, Alexander Kaplan <alexander.kaplan@sms-medipool.de>, Takashi Iwai <tiwai@suse.de>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806161428.1142408-1-sashal@kernel.org>
+
+From: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+
+[ 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 <alexander.kaplan@sms-medipool.de>
+Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/work_items/8412
+Tested-by: Alexander Kaplan <alexander.kaplan@sms-medipool.de>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
+Link: https://patch.msgid.link/20260715180610.1371243-1-kai.vehmanen@linux.intel.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+[ 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 <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
index f6e0f4fa943db218f2ec80311f1c0c90304eec26..391e9032a8307fb977c01877c0a749b9dabe9b47 100644 (file)
@@ -54,11 +54,9 @@ Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
 (cherry picked from commit a02b8950d619123da64f69b70fe1dadef217dfe4)
 Signed-off-by: Sasha Levin <sashal@kernel.org>
 ---
- 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 (file)
index 0000000..09d56bf
--- /dev/null
@@ -0,0 +1,166 @@
+From stable+bounces-297111-greg=kroah.com@vger.kernel.org Fri Aug  7 04:36:29 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <catalin.marinas@arm.com>, Ido Schimmel <idosch@idosch.org>, Ido Schimmel <idosch@nvidia.com>, Robin Murphy <robin.murphy@arm.com>, Joerg Roedel <joro@8bytes.org>, Will Deacon <will@kernel.org>, Andrew Morton <akpm@linux-foundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260807023615.1684238-1-sashal@kernel.org>
+
+From: Catalin Marinas <catalin.marinas@arm.com>
+
+[ 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:
+    [<ffffffff819f5f08>] __kmem_cache_alloc_node+0x1e8/0x320
+    [<ffffffff818a239a>] kmalloc_trace+0x2a/0x60
+    [<ffffffff8231d31e>] free_iova_fast+0x28e/0x4e0
+    [<ffffffff82310860>] fq_ring_free_locked+0x1b0/0x310
+    [<ffffffff8231225d>] fq_flush_timeout+0x19d/0x2e0
+    [<ffffffff813e95ba>] call_timer_fn+0x19a/0x5c0
+    [<ffffffff813ea16b>] __run_timers+0x78b/0xb80
+    [<ffffffff813ea5bd>] run_timer_softirq+0x5d/0xd0
+    [<ffffffff82f1d915>] __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 <catalin.marinas@arm.com>
+Reported-by: Ido Schimmel <idosch@idosch.org>
+Tested-by: Ido Schimmel <idosch@nvidia.com>
+Acked-by: Robin Murphy <robin.murphy@arm.com>
+Cc: Joerg Roedel <joro@8bytes.org>
+Cc: Will Deacon <will@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: 79c37ae3733e ("mm/kmemleak: fix checksum computation for per-cpu objects")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 <linux/iova.h>
++#include <linux/kmemleak.h>
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/smp.h>
+@@ -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 (file)
index 0000000..e57f832
--- /dev/null
@@ -0,0 +1,82 @@
+From stable+bounces-295133-greg=kroah.com@vger.kernel.org Tue Aug  4 13:27:56 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <jackson.lee@chipsnmedia.com>, Nas Chung <nas.chung@chipsnmedia.com>, Brandon Brnich <b-brnich@ti.com>, Nicolas Dufresne <nicolas.dufresne@collabora.com>, Hans Verkuil <hverkuil+cisco@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260804112144.2939763-1-sashal@kernel.org>
+
+From: Jackson Lee <jackson.lee@chipsnmedia.com>
+
+[ 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 <jackson.lee@chipsnmedia.com>
+Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
+Tested-by: Brandon Brnich <b-brnich@ti.com>
+Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..13b5af0
--- /dev/null
@@ -0,0 +1,124 @@
+From stable+bounces-294966-greg=kroah.com@vger.kernel.org Tue Aug  4 03:06:52 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <jai.luthra@ideasonboard.com>, Dave Stevenson <dave.stevenson@raspberrypi.com>, Sakari Ailus <sakari.ailus@linux.intel.com>, Hans Verkuil <hverkuil@xs4all.nl>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260804010642.2350939-1-sashal@kernel.org>
+
+From: Jai Luthra <jai.luthra@ideasonboard.com>
+
+[ Upstream commit 04f78503f99ae7e9887c7fe5e4bc54a7cfb10fe0 ]
+
+The IMX219 datasheet refers to the vertical length + blanking as
+FRM_LENGTH instead of VTS.
+
+Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Signed-off-by: Jai Luthra <jai.luthra@ideasonboard.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
+Stable-dep-of: 2c4f1ba73543 ("media: imx219: Fix maximum frame length in lines")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..974d239
--- /dev/null
@@ -0,0 +1,37 @@
+From stable+bounces-294967-greg=kroah.com@vger.kernel.org Tue Aug  4 03:09:49 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <sakari.ailus@linux.intel.com>, Dave Stevenson <dave.stevenson@raspberrypi.com>, Laurent Pinchart <laurent.pinchart@ideasonboard.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260804010642.2350939-2-sashal@kernel.org>
+
+From: Sakari Ailus <sakari.ailus@linux.intel.com>
+
+[ 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 <sakari.ailus@linux.intel.com>
+Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..67293d1
--- /dev/null
@@ -0,0 +1,56 @@
+From stable+bounces-296812-greg=kroah.com@vger.kernel.org Thu Aug  6 15:13:19 2026
+From: Sasha Levin <sashal@kernel.org>
+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" <niklas.soderlund+renesas@ragnatech.se>, "Laurent Pinchart" <laurent.pinchart@ideasonboard.com>, "Hans Verkuil" <hverkuil+cisco@kernel.org>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260806130655.335225-1-sashal@kernel.org>
+
+From: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
+
+[ 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 <niklas.soderlund+renesas@ragnatech.se>
+Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Link: https://patch.msgid.link/20260501190339.3449193-1-niklas.soderlund+renesas@ragnatech.se
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
+[ 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 <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..12ff291
--- /dev/null
@@ -0,0 +1,78 @@
+From stable+bounces-297112-greg=kroah.com@vger.kernel.org Fri Aug  7 04:37:09 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <leitao@debian.org>, Catalin Marinas <catalin.marinas@arm.com>, Pavel Tikhomirov <ptikhomirov@virtuozzo.com>, Andrew Morton <akpm@linux-foundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260807023615.1684238-2-sashal@kernel.org>
+
+From: Breno Leitao <leitao@debian.org>
+
+[ 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 <leitao@debian.org>
+Co-developed-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..966c857
--- /dev/null
@@ -0,0 +1,155 @@
+From stable+bounces-297105-greg=kroah.com@vger.kernel.org Fri Aug  7 04:36:38 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <tanggeliang@kylinos.cn>, "Matthieu Baerts (NGI0)" <matttbe@kernel.org>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260807023606.1683287-2-sashal@kernel.org>
+
+From: Geliang Tang <tanggeliang@kylinos.cn>
+
+[ 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 <tanggeliang@kylinos.cn>
+Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20241213-net-next-mptcp-pm-misc-cleanup-v1-1-ddb6d00109a8@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..d9e4450
--- /dev/null
@@ -0,0 +1,71 @@
+From stable+bounces-297104-greg=kroah.com@vger.kernel.org Fri Aug  7 04:36:36 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <tanggeliang@kylinos.cn>, "Matthieu Baerts (NGI0)" <matttbe@kernel.org>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260807023606.1683287-1-sashal@kernel.org>
+
+From: Geliang Tang <tanggeliang@kylinos.cn>
+
+[ 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) <matttbe@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20241115-net-next-mptcp-pm-lockless-dump-v1-2-f4a1bcb4ca2c@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..931b6f0
--- /dev/null
@@ -0,0 +1,155 @@
+From stable+bounces-297106-greg=kroah.com@vger.kernel.org Fri Aug  7 04:36:18 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <tanggeliang@kylinos.cn>, "Matthieu Baerts (NGI0)" <matttbe@kernel.org>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260807023606.1683287-3-sashal@kernel.org>
+
+From: Geliang Tang <tanggeliang@kylinos.cn>
+
+[ 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 <tanggeliang@kylinos.cn>
+Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20250307-net-next-mptcp-pm-reorg-v1-1-abef20ada03b@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..07cf879
--- /dev/null
@@ -0,0 +1,93 @@
+From stable+bounces-297107-greg=kroah.com@vger.kernel.org Fri Aug  7 04:36:20 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <tanggeliang@kylinos.cn>, Xuanqiang Luo <luoxuanqiang@kylinos.cn>, "Matthieu Baerts (NGI0)" <matttbe@kernel.org>, Jakub Kicinski <kuba@kernel.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260807023606.1683287-4-sashal@kernel.org>
+
+From: Geliang Tang <tanggeliang@kylinos.cn>
+
+[ 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]  <IRQ>
+  [  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 <tanggeliang@kylinos.cn>
+Tested-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
+Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
+Link: https://patch.msgid.link/20260722-net-mptcp-misc-fixes-7-2-rc5-v1-2-6fb595bc86ef@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
index 80d4091bb4a6bc3032e26d2c077c555f9a718154..9a8bbf3aa248d27a748b7ed6b28e53a7252b275b 100644 (file)
@@ -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 (file)
index 0000000..26505a8
--- /dev/null
@@ -0,0 +1,388 @@
+From sashal@kernel.org Thu Jul 30 21:29:51 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <zzzccc427@gmail.com>, stable <stable@kernel.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260730192948.3124448-1-sashal@kernel.org>
+
+From: Cen Zhang <zzzccc427@gmail.com>
+
+[ 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:
+ <TASK>
+ 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
+ </TASK>
+
+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 <stable@kernel.org>
+Assisted-by: Codex:gpt-5.5
+Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
+Link: https://patch.msgid.link/20260627104153.3822495-1-zzzccc427@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+[ 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 <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 <linux/kref.h>
++#include <linux/spinlock.h>
+ /* #include <linux/usb/uas.h> */
+ #include <linux/usb/composite.h>
+ #include <linux/usb/uas.h>
+@@ -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 (file)
index 0000000..58546c1
--- /dev/null
@@ -0,0 +1,173 @@
+From sashal@kernel.org Thu Jul 30 21:39:03 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <akuchynski@chromium.org>, stable <stable@kernel.org>, Benson Leung <bleung@chromium.org>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260730193859.3130212-2-sashal@kernel.org>
+
+From: Andrei Kuchynski <akuchynski@chromium.org>
+
+[ 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:
+    <IRQ>
+    __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:
+  <TASK>
+    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 <stable@kernel.org>
+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 <akuchynski@chromium.org>
+Reviewed-by: Benson Leung <bleung@chromium.org>
+Link: https://patch.msgid.link/20260707141736.1635698-1-akuchynski@chromium.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..7f3ba7f
--- /dev/null
@@ -0,0 +1,124 @@
+From sashal@kernel.org Thu Jul 30 21:39:02 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <senozhatsky@chromium.org>, Heikki Krogerus <heikki.krogerus@linux.intel.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260730193859.3130212-1-sashal@kernel.org>
+
+From: Sergey Senozhatsky <senozhatsky@chromium.org>
+
+[ 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]  <TASK>
+[    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]  </TASK>
+[    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 <senozhatsky@chromium.org>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260515060042.136083-1-senozhatsky@chromium.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 7aa7d4bf9d3f ("usb: typec: ucsi: Fix race condition and ordering in port unregistration")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..00eb3b1
--- /dev/null
@@ -0,0 +1,50 @@
+From stable+bounces-296831-greg=kroah.com@vger.kernel.org Thu Aug  6 15:51:53 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <git@danielhodges.dev>, Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>, Jeff Johnson <jeff.johnson@oss.qualcomm.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806134431.555434-1-sashal@kernel.org>
+
+From: Daniel Hodges <git@danielhodges.dev>
+
+[ 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 <git@danielhodges.dev>
+Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
+Link: https://patch.msgid.link/20260206185207.30098-1-git@danielhodges.dev
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..9925647
--- /dev/null
@@ -0,0 +1,292 @@
+From stable+bounces-296820-greg=kroah.com@vger.kernel.org Thu Aug  6 15:32:56 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <fanwu01@zju.edu.cn>, Arend van Spriel <arend.vanspriel@broadcom.com>, Johannes Berg <johannes.berg@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806132230.416656-1-sashal@kernel.org>
+
+From: Fan Wu <fanwu01@zju.edu.cn>
+
+[ 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 <fanwu01@zju.edu.cn>
+Assisted-by: Codex:gpt-5.6
+Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
+Link: https://patch.msgid.link/20260718024353.3147201-3-fanwu01@zju.edu.cn
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 <linux/kernel.h>
+ #include <linux/firmware.h>
+ #include <linux/device.h>
++#include <linux/mutex.h>
+ #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 (file)
index 0000000..f4ef305
--- /dev/null
@@ -0,0 +1,130 @@
+From stable+bounces-296832-greg=kroah.com@vger.kernel.org Thu Aug  6 15:50:30 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <gokulkumar.sivakumar@infineon.com>, Arend van Spriel <arend.vanspriel@broadcom.com>, Johannes Berg <johannes.berg@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806134937.599576-1-sashal@kernel.org>
+
+From: Gokul Sivakumar <gokulkumar.sivakumar@infineon.com>
+
+[ 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 <gokulkumar.sivakumar@infineon.com>
+Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
+Link: https://patch.msgid.link/20250724101136.6691-1-gokulkumar.sivakumar@infineon.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Stable-dep-of: 29ab31f3f271 ("wifi: brcmfmac: set F2 blocksize to 256 for BCM43752")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..3a3476b
--- /dev/null
@@ -0,0 +1,59 @@
+From stable+bounces-296833-greg=kroah.com@vger.kernel.org Thu Aug  6 15:50:34 2026
+From: Sasha Levin <sashal@kernel.org>
+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 <zaq14760@gmail.com>, Arend van Spriel <arend.vanspriel@broadcom.com>, Johannes Berg <johannes.berg@intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260806134937.599576-2-sashal@kernel.org>
+
+From: LiangCheng Wang <zaq14760@gmail.com>
+
+[ 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 <zaq14760@gmail.com>
+Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
+Link: https://patch.msgid.link/20260715-b43752-f2-blksz-v2-1-f9be49856050@gmail.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;