From: Greg Kroah-Hartman Date: Tue, 17 Mar 2026 11:24:39 +0000 (+0100) Subject: 6.19-stable patches X-Git-Tag: v6.18.19~56 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=04d68e672d434dc2640ccbbe381381b6c6c5c3a9;p=thirdparty%2Fkernel%2Fstable-queue.git 6.19-stable patches added patches: kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch liveupdate-luo_file-remember-retrieve-status.patch mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch nsfs-tighten-permission-checks-for-ns-iteration-ioctls.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 --- diff --git a/queue-6.19/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch b/queue-6.19/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch new file mode 100644 index 0000000000..374a352fc1 --- /dev/null +++ b/queue-6.19/kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch @@ -0,0 +1,213 @@ +From 28aaa9c39945b7925a1cc1d513c8f21ed38f5e4f Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Thu, 26 Feb 2026 10:43:55 +0100 +Subject: kthread: consolidate kthread exit paths to prevent use-after-free + +From: Christian Brauner + +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 +Tested-by: Guillaume Tucker +Tested-by: Mark Brown +Tested-by: David Gow +Cc: +Link: https://lore.kernel.org/all/20260224-mittlerweile-besessen-2738831ae7f6@brauner +Co-developed-by: Linus Torvalds +Fixes: 4d13f4304fa4 ("kthread: Implement preferred affinity") +Signed-off-by: Linus Torvalds +Signed-off-by: Christian Brauner +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -896,11 +896,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); + +@@ -1013,6 +1018,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. +@@ -680,7 +649,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.19/liveupdate-luo_file-remember-retrieve-status.patch b/queue-6.19/liveupdate-luo_file-remember-retrieve-status.patch new file mode 100644 index 0000000000..bc16f4dc9a --- /dev/null +++ b/queue-6.19/liveupdate-luo_file-remember-retrieve-status.patch @@ -0,0 +1,208 @@ +From f85b1c6af5bc3872f994df0a5688c1162de07a62 Mon Sep 17 00:00:00 2001 +From: "Pratyush Yadav (Google)" +Date: Mon, 16 Feb 2026 14:22:19 +0100 +Subject: liveupdate: luo_file: remember retrieve() status + +From: Pratyush Yadav (Google) + +commit f85b1c6af5bc3872f994df0a5688c1162de07a62 upstream. + +LUO keeps track of successful retrieve attempts on a LUO file. It does so +to avoid multiple retrievals of the same file. Multiple retrievals cause +problems because once the file is retrieved, the serialized data +structures are likely freed and the file is likely in a very different +state from what the code expects. + +The retrieve boolean in struct luo_file keeps track of this, and is passed +to the finish callback so it knows what work was already done and what it +has left to do. + +All this works well when retrieve succeeds. When it fails, +luo_retrieve_file() returns the error immediately, without ever storing +anywhere that a retrieve was attempted or what its error code was. This +results in an errored LIVEUPDATE_SESSION_RETRIEVE_FD ioctl to userspace, +but nothing prevents it from trying this again. + +The retry is problematic for much of the same reasons listed above. The +file is likely in a very different state than what the retrieve logic +normally expects, and it might even have freed some serialization data +structures. Attempting to access them or free them again is going to +break things. + +For example, if memfd managed to restore 8 of its 10 folios, but fails on +the 9th, a subsequent retrieve attempt will try to call +kho_restore_folio() on the first folio again, and that will fail with a +warning since it is an invalid operation. + +Apart from the retry, finish() also breaks. Since on failure the +retrieved bool in luo_file is never touched, the finish() call on session +close will tell the file handler that retrieve was never attempted, and it +will try to access or free the data structures that might not exist, much +in the same way as the retry attempt. + +There is no sane way of attempting the retrieve again. Remember the error +retrieve returned and directly return it on a retry. Also pass this +status code to finish() so it can make the right decision on the work it +needs to do. + +This is done by changing the bool to an integer. A value of 0 means +retrieve was never attempted, a positive value means it succeeded, and a +negative value means it failed and the error code is the value. + +Link: https://lkml.kernel.org/r/20260216132221.987987-1-pratyush@kernel.org +Fixes: 7c722a7f44e0 ("liveupdate: luo_file: implement file systems callbacks") +Signed-off-by: Pratyush Yadav (Google) +Reviewed-by: Mike Rapoport (Microsoft) +Cc: Pasha Tatashin +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/liveupdate.h | 9 ++++++--- + kernel/liveupdate/luo_file.c | 41 +++++++++++++++++++++++++---------------- + mm/memfd_luo.c | 7 ++++++- + 3 files changed, 37 insertions(+), 20 deletions(-) + +--- a/include/linux/liveupdate.h ++++ b/include/linux/liveupdate.h +@@ -20,8 +20,11 @@ struct file; + /** + * struct liveupdate_file_op_args - Arguments for file operation callbacks. + * @handler: The file handler being called. +- * @retrieved: The retrieve status for the 'can_finish / finish' +- * operation. ++ * @retrieve_status: The retrieve status for the 'can_finish / finish' ++ * operation. A value of 0 means the retrieve has not been ++ * attempted, a positive value means the retrieve was ++ * successful, and a negative value means the retrieve failed, ++ * and the value is the error code of the call. + * @file: The file object. For retrieve: [OUT] The callback sets + * this to the new file. For other ops: [IN] The caller sets + * this to the file being operated on. +@@ -37,7 +40,7 @@ struct file; + */ + struct liveupdate_file_op_args { + struct liveupdate_file_handler *handler; +- bool retrieved; ++ int retrieve_status; + struct file *file; + u64 serialized_data; + void *private_data; +--- a/kernel/liveupdate/luo_file.c ++++ b/kernel/liveupdate/luo_file.c +@@ -133,9 +133,12 @@ static LIST_HEAD(luo_file_handler_list); + * state that is not preserved. Set by the handler's .preserve() + * callback, and must be freed in the handler's .unpreserve() + * callback. +- * @retrieved: A flag indicating whether a user/kernel in the new kernel has ++ * @retrieve_status: Status code indicating whether a user/kernel in the new kernel has + * successfully called retrieve() on this file. This prevents +- * multiple retrieval attempts. ++ * multiple retrieval attempts. A value of 0 means a retrieve() ++ * has not been attempted, a positive value means the retrieve() ++ * was successful, and a negative value means the retrieve() ++ * failed, and the value is the error code of the call. + * @mutex: A mutex that protects the fields of this specific instance + * (e.g., @retrieved, @file), ensuring that operations like + * retrieving or finishing a file are atomic. +@@ -160,7 +163,7 @@ struct luo_file { + struct file *file; + u64 serialized_data; + void *private_data; +- bool retrieved; ++ int retrieve_status; + struct mutex mutex; + struct list_head list; + u64 token; +@@ -293,7 +296,6 @@ int luo_preserve_file(struct luo_file_se + luo_file->file = file; + luo_file->fh = fh; + luo_file->token = token; +- luo_file->retrieved = false; + mutex_init(&luo_file->mutex); + + args.handler = fh; +@@ -569,7 +571,12 @@ int luo_retrieve_file(struct luo_file_se + return -ENOENT; + + guard(mutex)(&luo_file->mutex); +- if (luo_file->retrieved) { ++ if (luo_file->retrieve_status < 0) { ++ /* Retrieve was attempted and it failed. Return the error code. */ ++ return luo_file->retrieve_status; ++ } ++ ++ if (luo_file->retrieve_status > 0) { + /* + * Someone is asking for this file again, so get a reference + * for them. +@@ -582,16 +589,19 @@ int luo_retrieve_file(struct luo_file_se + args.handler = luo_file->fh; + args.serialized_data = luo_file->serialized_data; + err = luo_file->fh->ops->retrieve(&args); +- if (!err) { +- luo_file->file = args.file; +- +- /* Get reference so we can keep this file in LUO until finish */ +- get_file(luo_file->file); +- *filep = luo_file->file; +- luo_file->retrieved = true; ++ if (err) { ++ /* Keep the error code for later use. */ ++ luo_file->retrieve_status = err; ++ return err; + } + +- return err; ++ luo_file->file = args.file; ++ /* Get reference so we can keep this file in LUO until finish */ ++ get_file(luo_file->file); ++ *filep = luo_file->file; ++ luo_file->retrieve_status = 1; ++ ++ return 0; + } + + static int luo_file_can_finish_one(struct luo_file_set *file_set, +@@ -607,7 +617,7 @@ static int luo_file_can_finish_one(struc + args.handler = luo_file->fh; + args.file = luo_file->file; + args.serialized_data = luo_file->serialized_data; +- args.retrieved = luo_file->retrieved; ++ args.retrieve_status = luo_file->retrieve_status; + can_finish = luo_file->fh->ops->can_finish(&args); + } + +@@ -624,7 +634,7 @@ static void luo_file_finish_one(struct l + args.handler = luo_file->fh; + args.file = luo_file->file; + args.serialized_data = luo_file->serialized_data; +- args.retrieved = luo_file->retrieved; ++ args.retrieve_status = luo_file->retrieve_status; + + luo_file->fh->ops->finish(&args); + } +@@ -779,7 +789,6 @@ int luo_file_deserialize(struct luo_file + luo_file->file = NULL; + luo_file->serialized_data = file_ser[i].data; + luo_file->token = file_ser[i].token; +- luo_file->retrieved = false; + mutex_init(&luo_file->mutex); + list_add_tail(&luo_file->list, &file_set->files_list); + } +--- a/mm/memfd_luo.c ++++ b/mm/memfd_luo.c +@@ -326,7 +326,12 @@ static void memfd_luo_finish(struct live + struct memfd_luo_folio_ser *folios_ser; + struct memfd_luo_ser *ser; + +- if (args->retrieved) ++ /* ++ * If retrieve was successful, nothing to do. If it failed, retrieve() ++ * already cleaned up everything it could. So nothing to do there ++ * either. Only need to clean up when retrieve was not called. ++ */ ++ if (args->retrieve_status) + return; + + ser = phys_to_virt(args->serialized_data); diff --git a/queue-6.19/mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch b/queue-6.19/mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch new file mode 100644 index 0000000000..e8c6437553 --- /dev/null +++ b/queue-6.19/mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch @@ -0,0 +1,223 @@ +From b570f37a2ce480be26c665345c5514686a8a0274 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thomas=20Hellstr=C3=B6m?= +Date: Tue, 10 Feb 2026 12:56:53 +0100 +Subject: mm: Fix a hmm_range_fault() livelock / starvation problem +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +From: Thomas Hellström + +commit b570f37a2ce480be26c665345c5514686a8a0274 upstream. + +If hmm_range_fault() fails a folio_trylock() in do_swap_page, +trying to acquire the lock of a device-private folio for migration, +to ram, the function will spin until it succeeds grabbing the lock. + +However, if the process holding the lock is depending on a work +item to be completed, which is scheduled on the same CPU as the +spinning hmm_range_fault(), that work item might be starved and +we end up in a livelock / starvation situation which is never +resolved. + +This can happen, for example if the process holding the +device-private folio lock is stuck in + migrate_device_unmap()->lru_add_drain_all() +sinc lru_add_drain_all() requires a short work-item +to be run on all online cpus to complete. + +A prerequisite for this to happen is: +a) Both zone device and system memory folios are considered in + migrate_device_unmap(), so that there is a reason to call + lru_add_drain_all() for a system memory folio while a + folio lock is held on a zone device folio. +b) The zone device folio has an initial mapcount > 1 which causes + at least one migration PTE entry insertion to be deferred to + try_to_migrate(), which can happen after the call to + lru_add_drain_all(). +c) No or voluntary only preemption. + +This all seems pretty unlikely to happen, but indeed is hit by +the "xe_exec_system_allocator" igt test. + +Resolve this by waiting for the folio to be unlocked if the +folio_trylock() fails in do_swap_page(). + +Rename migration_entry_wait_on_locked() to +softleaf_entry_wait_unlock() and update its documentation to +indicate the new use-case. + +Future code improvements might consider moving +the lru_add_drain_all() call in migrate_device_unmap() to be +called *after* all pages have migration entries inserted. +That would eliminate also b) above. + +v2: +- Instead of a cond_resched() in hmm_range_fault(), + eliminate the problem by waiting for the folio to be unlocked + in do_swap_page() (Alistair Popple, Andrew Morton) +v3: +- Add a stub migration_entry_wait_on_locked() for the + !CONFIG_MIGRATION case. (Kernel Test Robot) +v4: +- Rename migrate_entry_wait_on_locked() to + softleaf_entry_wait_on_locked() and update docs (Alistair Popple) +v5: +- Add a WARN_ON_ONCE() for the !CONFIG_MIGRATION + version of softleaf_entry_wait_on_locked(). +- Modify wording around function names in the commit message + (Andrew Morton) + +Suggested-by: Alistair Popple +Fixes: 1afaeb8293c9 ("mm/migrate: Trylock device page in do_swap_page") +Cc: Ralph Campbell +Cc: Christoph Hellwig +Cc: Jason Gunthorpe +Cc: Jason Gunthorpe +Cc: Leon Romanovsky +Cc: Andrew Morton +Cc: Matthew Brost +Cc: John Hubbard +Cc: Alistair Popple +Cc: linux-mm@kvack.org +Cc: +Signed-off-by: Thomas Hellström +Cc: # v6.15+ +Reviewed-by: John Hubbard #v3 +Reviewed-by: Alistair Popple +Link: https://patch.msgid.link/20260210115653.92413-1-thomas.hellstrom@linux.intel.com +(cherry picked from commit a69d1ab971a624c6f112cea61536569d579c3215) +Signed-off-by: Rodrigo Vivi +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/migrate.h | 10 +++++++++- + mm/filemap.c | 15 ++++++++++----- + mm/memory.c | 3 ++- + mm/migrate.c | 8 ++++---- + mm/migrate_device.c | 2 +- + 5 files changed, 26 insertions(+), 12 deletions(-) + +--- a/include/linux/migrate.h ++++ b/include/linux/migrate.h +@@ -65,7 +65,7 @@ bool isolate_folio_to_list(struct folio + + int migrate_huge_page_move_mapping(struct address_space *mapping, + struct folio *dst, struct folio *src); +-void migration_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl) ++void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl) + __releases(ptl); + void folio_migrate_flags(struct folio *newfolio, struct folio *folio); + int folio_migrate_mapping(struct address_space *mapping, +@@ -97,6 +97,14 @@ static inline int set_movable_ops(const + return -ENOSYS; + } + ++static inline void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl) ++ __releases(ptl) ++{ ++ WARN_ON_ONCE(1); ++ ++ spin_unlock(ptl); ++} ++ + #endif /* CONFIG_MIGRATION */ + + #ifdef CONFIG_NUMA_BALANCING +--- a/mm/filemap.c ++++ b/mm/filemap.c +@@ -1379,14 +1379,16 @@ repeat: + + #ifdef CONFIG_MIGRATION + /** +- * migration_entry_wait_on_locked - Wait for a migration entry to be removed +- * @entry: migration swap entry. ++ * softleaf_entry_wait_on_locked - Wait for a migration entry or ++ * device_private entry to be removed. ++ * @entry: migration or device_private swap entry. + * @ptl: already locked ptl. This function will drop the lock. + * +- * Wait for a migration entry referencing the given page to be removed. This is ++ * Wait for a migration entry referencing the given page, or device_private ++ * entry referencing a dvice_private page to be unlocked. This is + * equivalent to folio_put_wait_locked(folio, TASK_UNINTERRUPTIBLE) except + * this can be called without taking a reference on the page. Instead this +- * should be called while holding the ptl for the migration entry referencing ++ * should be called while holding the ptl for @entry referencing + * the page. + * + * Returns after unlocking the ptl. +@@ -1394,7 +1396,7 @@ repeat: + * This follows the same logic as folio_wait_bit_common() so see the comments + * there. + */ +-void migration_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl) ++void softleaf_entry_wait_on_locked(softleaf_t entry, spinlock_t *ptl) + __releases(ptl) + { + struct wait_page_queue wait_page; +@@ -1428,6 +1430,9 @@ void migration_entry_wait_on_locked(soft + * If a migration entry exists for the page the migration path must hold + * a valid reference to the page, and it must take the ptl to remove the + * migration entry. So the page is valid until the ptl is dropped. ++ * Similarly any path attempting to drop the last reference to a ++ * device-private page needs to grab the ptl to remove the device-private ++ * entry. + */ + spin_unlock(ptl); + +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -4684,7 +4684,8 @@ vm_fault_t do_swap_page(struct vm_fault + unlock_page(vmf->page); + put_page(vmf->page); + } else { +- pte_unmap_unlock(vmf->pte, vmf->ptl); ++ pte_unmap(vmf->pte); ++ softleaf_entry_wait_on_locked(entry, vmf->ptl); + } + } else if (softleaf_is_hwpoison(entry)) { + ret = VM_FAULT_HWPOISON; +--- a/mm/migrate.c ++++ b/mm/migrate.c +@@ -499,7 +499,7 @@ void migration_entry_wait(struct mm_stru + if (!softleaf_is_migration(entry)) + goto out; + +- migration_entry_wait_on_locked(entry, ptl); ++ softleaf_entry_wait_on_locked(entry, ptl); + return; + out: + spin_unlock(ptl); +@@ -531,10 +531,10 @@ void migration_entry_wait_huge(struct vm + * If migration entry existed, safe to release vma lock + * here because the pgtable page won't be freed without the + * pgtable lock released. See comment right above pgtable +- * lock release in migration_entry_wait_on_locked(). ++ * lock release in softleaf_entry_wait_on_locked(). + */ + hugetlb_vma_unlock_read(vma); +- migration_entry_wait_on_locked(entry, ptl); ++ softleaf_entry_wait_on_locked(entry, ptl); + return; + } + +@@ -552,7 +552,7 @@ void pmd_migration_entry_wait(struct mm_ + ptl = pmd_lock(mm, pmd); + if (!pmd_is_migration_entry(*pmd)) + goto unlock; +- migration_entry_wait_on_locked(softleaf_from_pmd(*pmd), ptl); ++ softleaf_entry_wait_on_locked(softleaf_from_pmd(*pmd), ptl); + return; + unlock: + spin_unlock(ptl); +--- a/mm/migrate_device.c ++++ b/mm/migrate_device.c +@@ -176,7 +176,7 @@ static int migrate_vma_collect_huge_pmd( + } + + if (softleaf_is_migration(entry)) { +- migration_entry_wait_on_locked(entry, ptl); ++ softleaf_entry_wait_on_locked(entry, ptl); + spin_unlock(ptl); + return -EAGAIN; + } diff --git a/queue-6.19/nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch b/queue-6.19/nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch new file mode 100644 index 0000000000..12a1ae81bb --- /dev/null +++ b/queue-6.19/nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch @@ -0,0 +1,78 @@ +From e6b899f08066e744f89df16ceb782e06868bd148 Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Thu, 26 Feb 2026 14:50:09 +0100 +Subject: nsfs: tighten permission checks for ns iteration ioctls + +From: Christian Brauner + +commit e6b899f08066e744f89df16ceb782e06868bd148 upstream. + +Even privileged services should not necessarily be able to see other +privileged service's namespaces so they can't leak information to each +other. Use may_see_all_namespaces() helper that centralizes this policy +until the nstree adapts. + +Link: https://patch.msgid.link/20260226-work-visibility-fixes-v1-1-d2c2853313bd@kernel.org +Fixes: a1d220d9dafa ("nsfs: iterate through mount namespaces") +Reviewed-by: Jeff Layton +Cc: stable@kernel.org # v6.12+ +Signed-off-by: Christian Brauner +Signed-off-by: Greg Kroah-Hartman +--- + fs/nsfs.c | 13 +++++++++++++ + include/linux/ns_common.h | 2 ++ + kernel/nscommon.c | 6 ++++++ + 3 files changed, 21 insertions(+) + +--- a/fs/nsfs.c ++++ b/fs/nsfs.c +@@ -186,6 +186,17 @@ static bool nsfs_ioctl_valid(unsigned in + return false; + } + ++static bool may_use_nsfs_ioctl(unsigned int cmd) ++{ ++ switch (_IOC_NR(cmd)) { ++ case _IOC_NR(NS_MNT_GET_NEXT): ++ fallthrough; ++ case _IOC_NR(NS_MNT_GET_PREV): ++ return may_see_all_namespaces(); ++ } ++ return true; ++} ++ + static long ns_ioctl(struct file *filp, unsigned int ioctl, + unsigned long arg) + { +@@ -201,6 +212,8 @@ static long ns_ioctl(struct file *filp, + + if (!nsfs_ioctl_valid(ioctl)) + return -ENOIOCTLCMD; ++ if (!may_use_nsfs_ioctl(ioctl)) ++ return -EPERM; + + ns = get_proc_ns(file_inode(filp)); + switch (ioctl) { +--- a/include/linux/ns_common.h ++++ b/include/linux/ns_common.h +@@ -55,6 +55,8 @@ static __always_inline bool is_ns_init_i + + #define ns_common_free(__ns) __ns_common_free(to_ns_common((__ns))) + ++bool may_see_all_namespaces(void); ++ + static __always_inline __must_check int __ns_ref_active_read(const struct ns_common *ns) + { + return atomic_read(&ns->__ns_ref_active); +--- a/kernel/nscommon.c ++++ b/kernel/nscommon.c +@@ -309,3 +309,9 @@ void __ns_ref_active_get(struct ns_commo + return; + } + } ++ ++bool may_see_all_namespaces(void) ++{ ++ return (task_active_pid_ns(current) == &init_pid_ns) && ++ ns_capable_noaudit(init_pid_ns.user_ns, CAP_SYS_ADMIN); ++} diff --git a/queue-6.19/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch b/queue-6.19/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch new file mode 100644 index 0000000000..cf765a3635 --- /dev/null +++ b/queue-6.19/revert-ptdesc-remove-references-to-folios-from-__pagetable_ctor-and-pagetable_dtor.patch @@ -0,0 +1,83 @@ +From 2d28ed588f8d7d0d41b0a4fad7f0d05e4bbf1797 Mon Sep 17 00:00:00 2001 +From: Axel Rasmussen +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 + +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 +Acked-by: Shakeel Butt +Acked-by: Johannes Weiner +Reviewed-by: Vishal Moola (Oracle) +Cc: David Hildenbrand +Cc: Liam Howlett +Cc: Lorenzo Stoakes +Cc: Matthew Wilcox (Oracle) +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Suren Baghdasaryan +Cc: Vlastimil Babka +Cc: Roman Gushchin +Cc: Muchun Song +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/mm.h | 17 ++++++----------- + 1 file changed, 6 insertions(+), 11 deletions(-) + +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -3304,26 +3304,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.19/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch b/queue-6.19/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch new file mode 100644 index 0000000000..31cdd94117 --- /dev/null +++ b/queue-6.19/revert-tcpm-allow-looking-for-role_sw-device-in-the-main-node.patch @@ -0,0 +1,42 @@ +From 6b275bfaa16be3fb1689fa6794e445ecd127a1b4 Mon Sep 17 00:00:00 2001 +From: Xu Yang +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 + +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 +Signed-off-by: Xu Yang +Tested-by: Arnaud Ferraris +Reviewed-by: Heikki Krogerus +Link: https://patch.msgid.link/20260309074313.2809867-2-xu.yang_2@nxp.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -7890,7 +7890,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); diff --git a/queue-6.19/series b/queue-6.19/series index a42cf36b54..2840705453 100644 --- a/queue-6.19/series +++ b/queue-6.19/series @@ -208,3 +208,9 @@ libceph-reject-preamble-if-control-segment-is-empty.patch libceph-prevent-potential-out-of-bounds-reads-in-process_message_header.patch libceph-use-u32-for-non-negative-values-in-ceph_monmap_decode.patch libceph-admit-message-frames-only-in-ceph_con_s_open-state.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 +mm-fix-a-hmm_range_fault-livelock-starvation-problem.patch +nsfs-tighten-permission-checks-for-ns-iteration-ioctls.patch +liveupdate-luo_file-remember-retrieve-status.patch +kthread-consolidate-kthread-exit-paths-to-prevent-use-after-free.patch