]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 17 Mar 2026 11:24:26 +0000 (12:24 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 17 Mar 2026 11:24:26 +0000 (12:24 +0100)
added patches:
kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch
revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch
revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch

queue-6.18/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch [new file with mode: 0644]
queue-6.18/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch [new file with mode: 0644]
queue-6.18/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch [new file with mode: 0644]
queue-6.18/series

diff --git a/queue-6.18/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch b/queue-6.18/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch
new file mode 100644 (file)
index 0000000..c971878
--- /dev/null
@@ -0,0 +1,213 @@
+From 28aaa9c39945b7925a1cc1d513c8f21ed38f5e4f Mon Sep 17 00:00:00 2001
+From: Christian Brauner <brauner@kernel.org>
+Date: Thu, 26 Feb 2026 10:43:55 +0100
+Subject: kthread: consolidate kthread exit paths to prevent use-after-free
+
+From: Christian Brauner <brauner@kernel.org>
+
+commit 28aaa9c39945b7925a1cc1d513c8f21ed38f5e4f upstream.
+
+Guillaume reported crashes via corrupted RCU callback function pointers
+during KUnit testing. The crash was traced back to the pidfs rhashtable
+conversion which replaced the 24-byte rb_node with an 8-byte rhash_head
+in struct pid, shrinking it from 160 to 144 bytes.
+
+struct kthread (without CONFIG_BLK_CGROUP) is also 144 bytes. With
+CONFIG_SLAB_MERGE_DEFAULT and SLAB_HWCACHE_ALIGN both round up to
+192 bytes and share the same slab cache. struct pid.rcu.func and
+struct kthread.affinity_node both sit at offset 0x78.
+
+When a kthread exits via make_task_dead() it bypasses kthread_exit() and
+misses the affinity_node cleanup. free_kthread_struct() frees the memory
+while the node is still linked into the global kthread_affinity_list. A
+subsequent list_del() by another kthread writes through dangling list
+pointers into the freed and reused memory, corrupting the pid's
+rcu.func pointer.
+
+Instead of patching free_kthread_struct() to handle the missed cleanup,
+consolidate all kthread exit paths. Turn kthread_exit() into a macro
+that calls do_exit() and add kthread_do_exit() which is called from
+do_exit() for any task with PF_KTHREAD set. This guarantees that
+kthread-specific cleanup always happens regardless of the exit path -
+make_task_dead(), direct do_exit(), or kthread_exit().
+
+Replace __to_kthread() with a new tsk_is_kthread() accessor in the
+public header. Export do_exit() since module code using the
+kthread_exit() macro now needs it directly.
+
+Reported-by: Guillaume Tucker <gtucker@gtucker.io>
+Tested-by: Guillaume Tucker <gtucker@gtucker.io>
+Tested-by: Mark Brown <broonie@kernel.org>
+Tested-by: David Gow <davidgow@google.com>
+Cc: <stable@vger.kernel.org>
+Link: https://lore.kernel.org/all/20260224-mittlerweile-besessen-2738831ae7f6@brauner
+Co-developed-by: Linus Torvalds <torvalds@linux-foundation.org>
+Fixes: 4d13f4304fa4 ("kthread: Implement preferred affinity")
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/kthread.h |   21 ++++++++++++++++++++-
+ kernel/exit.c           |    6 ++++++
+ kernel/kthread.c        |   41 +++++------------------------------------
+ 3 files changed, 31 insertions(+), 37 deletions(-)
+
+--- a/include/linux/kthread.h
++++ b/include/linux/kthread.h
+@@ -7,6 +7,24 @@
+ struct mm_struct;
++/* opaque kthread data */
++struct kthread;
++
++/*
++ * When "(p->flags & PF_KTHREAD)" is set the task is a kthread and will
++ * always remain a kthread.  For kthreads p->worker_private always
++ * points to a struct kthread.  For tasks that are not kthreads
++ * p->worker_private is used to point to other things.
++ *
++ * Return NULL for any task that is not a kthread.
++ */
++static inline struct kthread *tsk_is_kthread(struct task_struct *p)
++{
++      if (p->flags & PF_KTHREAD)
++              return p->worker_private;
++      return NULL;
++}
++
+ __printf(4, 5)
+ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
+                                          void *data,
+@@ -98,8 +116,9 @@ void *kthread_probe_data(struct task_str
+ int kthread_park(struct task_struct *k);
+ void kthread_unpark(struct task_struct *k);
+ void kthread_parkme(void);
+-void kthread_exit(long result) __noreturn;
++#define kthread_exit(result) do_exit(result)
+ void kthread_complete_and_exit(struct completion *, long) __noreturn;
++void kthread_do_exit(struct kthread *, long);
+ int kthreadd(void *unused);
+ extern struct task_struct *kthreadd_task;
+--- a/kernel/exit.c
++++ b/kernel/exit.c
+@@ -897,11 +897,16 @@ static void synchronize_group_exit(struc
+ void __noreturn do_exit(long code)
+ {
+       struct task_struct *tsk = current;
++      struct kthread *kthread;
+       int group_dead;
+       WARN_ON(irqs_disabled());
+       WARN_ON(tsk->plug);
++      kthread = tsk_is_kthread(tsk);
++      if (unlikely(kthread))
++              kthread_do_exit(kthread, code);
++
+       kcov_task_exit(tsk);
+       kmsan_task_exit(tsk);
+@@ -1008,6 +1013,7 @@ void __noreturn do_exit(long code)
+       lockdep_free_task(tsk);
+       do_task_dead();
+ }
++EXPORT_SYMBOL(do_exit);
+ void __noreturn make_task_dead(int signr)
+ {
+--- a/kernel/kthread.c
++++ b/kernel/kthread.c
+@@ -85,24 +85,6 @@ static inline struct kthread *to_kthread
+       return k->worker_private;
+ }
+-/*
+- * Variant of to_kthread() that doesn't assume @p is a kthread.
+- *
+- * When "(p->flags & PF_KTHREAD)" is set the task is a kthread and will
+- * always remain a kthread.  For kthreads p->worker_private always
+- * points to a struct kthread.  For tasks that are not kthreads
+- * p->worker_private is used to point to other things.
+- *
+- * Return NULL for any task that is not a kthread.
+- */
+-static inline struct kthread *__to_kthread(struct task_struct *p)
+-{
+-      void *kthread = p->worker_private;
+-      if (kthread && !(p->flags & PF_KTHREAD))
+-              kthread = NULL;
+-      return kthread;
+-}
+-
+ void get_kthread_comm(char *buf, size_t buf_size, struct task_struct *tsk)
+ {
+       struct kthread *kthread = to_kthread(tsk);
+@@ -193,7 +175,7 @@ EXPORT_SYMBOL_GPL(kthread_should_park);
+ bool kthread_should_stop_or_park(void)
+ {
+-      struct kthread *kthread = __to_kthread(current);
++      struct kthread *kthread = tsk_is_kthread(current);
+       if (!kthread)
+               return false;
+@@ -234,7 +216,7 @@ EXPORT_SYMBOL_GPL(kthread_freezable_shou
+  */
+ void *kthread_func(struct task_struct *task)
+ {
+-      struct kthread *kthread = __to_kthread(task);
++      struct kthread *kthread = tsk_is_kthread(task);
+       if (kthread)
+               return kthread->threadfn;
+       return NULL;
+@@ -266,7 +248,7 @@ EXPORT_SYMBOL_GPL(kthread_data);
+  */
+ void *kthread_probe_data(struct task_struct *task)
+ {
+-      struct kthread *kthread = __to_kthread(task);
++      struct kthread *kthread = tsk_is_kthread(task);
+       void *data = NULL;
+       if (kthread)
+@@ -309,19 +291,8 @@ void kthread_parkme(void)
+ }
+ EXPORT_SYMBOL_GPL(kthread_parkme);
+-/**
+- * kthread_exit - Cause the current kthread return @result to kthread_stop().
+- * @result: The integer value to return to kthread_stop().
+- *
+- * While kthread_exit can be called directly, it exists so that
+- * functions which do some additional work in non-modular code such as
+- * module_put_and_kthread_exit can be implemented.
+- *
+- * Does not return.
+- */
+-void __noreturn kthread_exit(long result)
++void kthread_do_exit(struct kthread *kthread, long result)
+ {
+-      struct kthread *kthread = to_kthread(current);
+       kthread->result = result;
+       if (!list_empty(&kthread->hotplug_node)) {
+               mutex_lock(&kthreads_hotplug_lock);
+@@ -333,9 +304,7 @@ void __noreturn kthread_exit(long result
+                       kthread->preferred_affinity = NULL;
+               }
+       }
+-      do_exit(0);
+ }
+-EXPORT_SYMBOL(kthread_exit);
+ /**
+  * kthread_complete_and_exit - Exit the current kthread.
+@@ -682,7 +651,7 @@ void kthread_set_per_cpu(struct task_str
+ bool kthread_is_per_cpu(struct task_struct *p)
+ {
+-      struct kthread *kthread = __to_kthread(p);
++      struct kthread *kthread = tsk_is_kthread(p);
+       if (!kthread)
+               return false;
diff --git a/queue-6.18/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch b/queue-6.18/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch
new file mode 100644 (file)
index 0000000..9d1b12a
--- /dev/null
@@ -0,0 +1,83 @@
+From 2d28ed588f8d7d0d41b0a4fad7f0d05e4bbf1797 Mon Sep 17 00:00:00 2001
+From: Axel Rasmussen <axelrasmussen@google.com>
+Date: Tue, 24 Feb 2026 16:24:34 -0800
+Subject: Revert "ptdesc: remove references to folios from __pagetable_ctor() and pagetable_dtor()"
+
+From: Axel Rasmussen <axelrasmussen@google.com>
+
+commit 2d28ed588f8d7d0d41b0a4fad7f0d05e4bbf1797 upstream.
+
+This change swapped out mod_node_page_state for lruvec_stat_add_folio.
+But, these two APIs are not interchangeable: the lruvec version also
+increments memcg stats, in addition to "global" pgdat stats.
+
+So after this change, the "pagetables" memcg stat in memory.stat always
+yields "0", which is a userspace visible regression.
+
+I tried to look for a refactor where we add a variant of
+lruvec_stat_mod_folio which takes a pgdat and a memcg instead of a folio,
+to try to adhere to the spirit of the original patch.  But at the end of
+the day this just means we have to call folio_memcg(ptdesc_folio(ptdesc))
+anyway, which doesn't really accomplish much.
+
+This regression is visible in master as well as 6.18 stable, so CC stable
+too.
+
+Link: https://lkml.kernel.org/r/20260225002434.2953895-1-axelrasmussen@google.com
+Fixes: f0c92726e89f ("ptdesc: remove references to folios from __pagetable_ctor() and pagetable_dtor()")
+Signed-off-by: Axel Rasmussen <axelrasmussen@google.com>
+Acked-by: Shakeel Butt <shakeel.butt@linux.dev>
+Acked-by: Johannes Weiner <hannes@cmpxchg.org>
+Reviewed-by: Vishal Moola (Oracle) <vishal.moola@gmail.com>
+Cc: David Hildenbrand <david@kernel.org>
+Cc: Liam Howlett <liam.howlett@oracle.com>
+Cc: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
+Cc: Michal Hocko <mhocko@suse.com>
+Cc: Mike Rapoport <rppt@kernel.org>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Cc: Vlastimil Babka <vbabka@suse.cz>
+Cc: Roman Gushchin <roman.gushchin@linux.dev>
+Cc: Muchun Song <muchun.song@linux.dev>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/mm.h |   17 ++++++-----------
+ 1 file changed, 6 insertions(+), 11 deletions(-)
+
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -3138,26 +3138,21 @@ static inline bool ptlock_init(struct pt
+ static inline void ptlock_free(struct ptdesc *ptdesc) {}
+ #endif /* defined(CONFIG_SPLIT_PTE_PTLOCKS) */
+-static inline unsigned long ptdesc_nr_pages(const struct ptdesc *ptdesc)
+-{
+-      return compound_nr(ptdesc_page(ptdesc));
+-}
+-
+ static inline void __pagetable_ctor(struct ptdesc *ptdesc)
+ {
+-      pg_data_t *pgdat = NODE_DATA(memdesc_nid(ptdesc->pt_flags));
++      struct folio *folio = ptdesc_folio(ptdesc);
+-      __SetPageTable(ptdesc_page(ptdesc));
+-      mod_node_page_state(pgdat, NR_PAGETABLE, ptdesc_nr_pages(ptdesc));
++      __folio_set_pgtable(folio);
++      lruvec_stat_add_folio(folio, NR_PAGETABLE);
+ }
+ static inline void pagetable_dtor(struct ptdesc *ptdesc)
+ {
+-      pg_data_t *pgdat = NODE_DATA(memdesc_nid(ptdesc->pt_flags));
++      struct folio *folio = ptdesc_folio(ptdesc);
+       ptlock_free(ptdesc);
+-      __ClearPageTable(ptdesc_page(ptdesc));
+-      mod_node_page_state(pgdat, NR_PAGETABLE, -ptdesc_nr_pages(ptdesc));
++      __folio_clear_pgtable(folio);
++      lruvec_stat_sub_folio(folio, NR_PAGETABLE);
+ }
+ static inline void pagetable_dtor_free(struct ptdesc *ptdesc)
diff --git a/queue-6.18/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch b/queue-6.18/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch
new file mode 100644 (file)
index 0000000..7a428b0
--- /dev/null
@@ -0,0 +1,42 @@
+From 6b275bfaa16be3fb1689fa6794e445ecd127a1b4 Mon Sep 17 00:00:00 2001
+From: Xu Yang <xu.yang_2@nxp.com>
+Date: Mon, 9 Mar 2026 15:43:12 +0800
+Subject: Revert "tcpm: allow looking for role_sw device in the main node"
+
+From: Xu Yang <xu.yang_2@nxp.com>
+
+commit 6b275bfaa16be3fb1689fa6794e445ecd127a1b4 upstream.
+
+This reverts commit 1366cd228b0c67b60a2c0c26ef37fe9f7cfedb7f.
+
+The fwnode_usb_role_switch_get() returns NULL only if no connection is
+found, returns ERR_PTR(-EPROBE_DEFER) if connection is found but deferred
+probe is needed, or a valid pointer of usb_role_switch.
+
+When switching from a NULL check to IS_ERR_OR_NULL(), usb_role_switch_get()
+returns NULL and overwrites the ERR_PTR(-EPROBE_DEFER) returned by
+fwnode_usb_role_switch_get(). This causes the deferred probe indication to
+be lost, preventing the USB role switch from ever being retrieved.
+
+Fixes: 1366cd228b0c ("tcpm: allow looking for role_sw device in the main node")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
+Tested-by: Arnaud Ferraris <arnaud.ferraris@collabora.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260309074313.2809867-2-xu.yang_2@nxp.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/tcpm/tcpm.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -7877,7 +7877,7 @@ struct tcpm_port *tcpm_register_port(str
+       port->partner_desc.identity = &port->partner_ident;
+       port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode);
+-      if (IS_ERR_OR_NULL(port->role_sw))
++      if (!port->role_sw)
+               port->role_sw = usb_role_switch_get(port->dev);
+       if (IS_ERR(port->role_sw)) {
+               err = PTR_ERR(port->role_sw);
index d06a6f0aa097ba03b0e6856a7509109b4e347eae..a357e878bdc04315a0f14673ad02cf9e851ae4db 100644 (file)
@@ -176,3 +176,6 @@ i3c-dw-i3c-master-set-sir_reject-in-dat-on-device-at.patch
 powerpc-perf-check-that-current-mm-is-alive-before-g.patch
 scsi-ufs-core-fix-serror-in-ufshcd_rtc_work-during-u.patch
 scsi-hisi_sas-fix-null-pointer-exception-during-user.patch
+revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch
+revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch
+kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch