]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for all trees
authorSasha Levin <sashal@kernel.org>
Sun, 26 Jul 2026 21:24:30 +0000 (17:24 -0400)
committerSasha Levin <sashal@kernel.org>
Sun, 26 Jul 2026 21:24:30 +0000 (17:24 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
13 files changed:
queue-5.10/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch [new file with mode: 0644]
queue-5.10/series
queue-5.15/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch [new file with mode: 0644]
queue-5.15/series
queue-6.1/mm-refactor-mm_access-to-not-return-null.patch [new file with mode: 0644]
queue-6.1/series
queue-6.12/mm-refactor-mm_access-to-not-return-null.patch [new file with mode: 0644]
queue-6.12/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch [new file with mode: 0644]
queue-6.12/series
queue-6.18/fuse-fix-writeback-array-overflow-when-max_pages-is-.patch [new file with mode: 0644]
queue-6.18/series
queue-6.6/mm-refactor-mm_access-to-not-return-null.patch [new file with mode: 0644]
queue-6.6/series

diff --git a/queue-5.10/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch b/queue-5.10/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch
new file mode 100644 (file)
index 0000000..0d36a4a
--- /dev/null
@@ -0,0 +1,70 @@
+From b2d8d89ca4f8d971fd1298ed4bda41fde4935db3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 18:44:59 +0800
+Subject: KVM: VMX: Make vmread_error_trampoline() uncallable from C code
+
+From: Sean Christopherson <seanjc@google.com>
+
+[ Upstream commit 0b5e7a16a0a79a3742f0df9e45bca46f01b40e6a ]
+
+Declare vmread_error_trampoline() as an opaque symbol so that it cannot
+be called from C code, at least not without some serious fudging.  The
+trampoline always passes parameters on the stack so that the inline
+VMREAD sequence doesn't need to clobber registers.  regparm(0) was
+originally added to document the stack behavior, but it ended up being
+confusing because regparm(0) is a nop for 64-bit targets.
+
+Opportunustically wrap the trampoline and its declaration in #ifdeffery
+to make it even harder to invoke incorrectly, to document why it exists,
+and so that it's not left behind if/when CONFIG_CC_HAS_ASM_GOTO_OUTPUT
+is true for all supported toolchains.
+
+No functional change intended.
+
+Cc: Uros Bizjak <ubizjak@gmail.com>
+Signed-off-by: Sean Christopherson <seanjc@google.com>
+Link: https://lore.kernel.org/r/20220928232015.745948-1-seanjc@google.com
+[ Vivian: Adjust context. Remove check for CONFIG_CC_HAS_ASM_GOTO_OUTPUT
+  which does not apply for old kernel. ]
+Signed-off-by: Vivian Wang <dramforever@live.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kvm/vmx/vmx_ops.h | 16 ++++++++++++++--
+ 1 file changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/arch/x86/kvm/vmx/vmx_ops.h b/arch/x86/kvm/vmx/vmx_ops.h
+index 692b0c31c9c82d..b80902686c1e41 100644
+--- a/arch/x86/kvm/vmx/vmx_ops.h
++++ b/arch/x86/kvm/vmx/vmx_ops.h
+@@ -13,14 +13,26 @@
+ #define __ex(x) __kvm_handle_fault_on_reboot(x)
+ asmlinkage void vmread_error(unsigned long field, bool fault);
+-__attribute__((regparm(0))) void vmread_error_trampoline(unsigned long field,
+-                                                       bool fault);
+ void vmwrite_error(unsigned long field, unsigned long value);
+ void vmclear_error(struct vmcs *vmcs, u64 phys_addr);
+ void vmptrld_error(struct vmcs *vmcs, u64 phys_addr);
+ void invvpid_error(unsigned long ext, u16 vpid, gva_t gva);
+ void invept_error(unsigned long ext, u64 eptp, gpa_t gpa);
++/*
++ * The VMREAD error trampoline _always_ uses the stack to pass parameters, even
++ * for 64-bit targets.  Preserving all registers allows the VMREAD inline asm
++ * blob to avoid clobbering GPRs, which in turn allows the compiler to better
++ * optimize sequences of VMREADs.
++ *
++ * Declare the trampoline as an opaque label as it's not safe to call from C
++ * code; there is no way to tell the compiler to pass params on the stack for
++ * 64-bit targets.
++ *
++ * void vmread_error_trampoline(unsigned long field, bool fault);
++ */
++extern unsigned long vmread_error_trampoline;
++
+ static __always_inline void vmcs_check16(unsigned long field)
+ {
+       BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2000,
+-- 
+2.53.0
+
index c4a8a5edea85fa59b7a9bc6a27c85b5eab688766..f8ba8cc0a36378b4808d631939505cc3999d8843 100644 (file)
@@ -7,3 +7,4 @@ can-isotp-fix-use-after-free-race-with-concurrent-ne.patch
 can-isotp-serialize-tx-state-transitions-under-so-rx.patch
 input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
 input-ims-pcu-fix-logic-error-in-packet-reset.patch
+kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch
diff --git a/queue-5.15/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch b/queue-5.15/kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch
new file mode 100644 (file)
index 0000000..b31eb3f
--- /dev/null
@@ -0,0 +1,70 @@
+From cb150f985bd456ba99fe8c115425f02217e188bf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 26 Jul 2026 18:44:59 +0800
+Subject: KVM: VMX: Make vmread_error_trampoline() uncallable from C code
+
+From: Sean Christopherson <seanjc@google.com>
+
+[ Upstream commit 0b5e7a16a0a79a3742f0df9e45bca46f01b40e6a ]
+
+Declare vmread_error_trampoline() as an opaque symbol so that it cannot
+be called from C code, at least not without some serious fudging.  The
+trampoline always passes parameters on the stack so that the inline
+VMREAD sequence doesn't need to clobber registers.  regparm(0) was
+originally added to document the stack behavior, but it ended up being
+confusing because regparm(0) is a nop for 64-bit targets.
+
+Opportunustically wrap the trampoline and its declaration in #ifdeffery
+to make it even harder to invoke incorrectly, to document why it exists,
+and so that it's not left behind if/when CONFIG_CC_HAS_ASM_GOTO_OUTPUT
+is true for all supported toolchains.
+
+No functional change intended.
+
+Cc: Uros Bizjak <ubizjak@gmail.com>
+Signed-off-by: Sean Christopherson <seanjc@google.com>
+Link: https://lore.kernel.org/r/20220928232015.745948-1-seanjc@google.com
+[ Vivian: Adjust context. Remove check for CONFIG_CC_HAS_ASM_GOTO_OUTPUT
+  which does not apply for old kernel. ]
+Signed-off-by: Vivian Wang <dramforever@live.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kvm/vmx/vmx_ops.h | 16 ++++++++++++++--
+ 1 file changed, 14 insertions(+), 2 deletions(-)
+
+diff --git a/arch/x86/kvm/vmx/vmx_ops.h b/arch/x86/kvm/vmx/vmx_ops.h
+index 9e9ef47e988c12..6d342e4f592082 100644
+--- a/arch/x86/kvm/vmx/vmx_ops.h
++++ b/arch/x86/kvm/vmx/vmx_ops.h
+@@ -11,14 +11,26 @@
+ #include "x86.h"
+ asmlinkage void vmread_error(unsigned long field, bool fault);
+-__attribute__((regparm(0))) void vmread_error_trampoline(unsigned long field,
+-                                                       bool fault);
+ void vmwrite_error(unsigned long field, unsigned long value);
+ void vmclear_error(struct vmcs *vmcs, u64 phys_addr);
+ void vmptrld_error(struct vmcs *vmcs, u64 phys_addr);
+ void invvpid_error(unsigned long ext, u16 vpid, gva_t gva);
+ void invept_error(unsigned long ext, u64 eptp, gpa_t gpa);
++/*
++ * The VMREAD error trampoline _always_ uses the stack to pass parameters, even
++ * for 64-bit targets.  Preserving all registers allows the VMREAD inline asm
++ * blob to avoid clobbering GPRs, which in turn allows the compiler to better
++ * optimize sequences of VMREADs.
++ *
++ * Declare the trampoline as an opaque label as it's not safe to call from C
++ * code; there is no way to tell the compiler to pass params on the stack for
++ * 64-bit targets.
++ *
++ * void vmread_error_trampoline(unsigned long field, bool fault);
++ */
++extern unsigned long vmread_error_trampoline;
++
+ static __always_inline void vmcs_check16(unsigned long field)
+ {
+       BUILD_BUG_ON_MSG(__builtin_constant_p(field) && ((field) & 0x6001) == 0x2000,
+-- 
+2.53.0
+
index 18bca0a39a39fc45d4f4bb475094a3f0e045e40c..5cbf8214f8b326297f04660807ffea797ae725e0 100644 (file)
@@ -8,3 +8,4 @@ can-isotp-serialize-tx-state-transitions-under-so-rx.patch
 dmaengine-sh-rz-dmac-move-interrupt-request-after-ev.patch
 input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
 input-ims-pcu-fix-logic-error-in-packet-reset.patch
+kvm-vmx-make-vmread_error_trampoline-uncallable-from.patch
diff --git a/queue-6.1/mm-refactor-mm_access-to-not-return-null.patch b/queue-6.1/mm-refactor-mm_access-to-not-return-null.patch
new file mode 100644 (file)
index 0000000..888428c
--- /dev/null
@@ -0,0 +1,132 @@
+From 5309c17af0356d024321c89ff6c1771737e0c2cf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2024 21:10:23 +0100
+Subject: mm: refactor mm_access() to not return NULL
+
+From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+
+[ Upstream commit cd3f8467afd470ccab0de2fbc7c76664af4a0bac ]
+
+mm_access() can return NULL if the mm is not found, but this is handled
+the same as an error in all callers, with some translating this into an
+-ESRCH error.
+
+Only proc_mem_open() returns NULL if no mm is found, however in this case
+it is clearer and makes more sense to explicitly handle the error.
+Additionally we take the opportunity to refactor the function to eliminate
+unnecessary nesting.
+
+Simplify things by simply returning -ESRCH if no mm is found - this both
+eliminates confusing use of the IS_ERR_OR_NULL() macro, and simplifies
+callers which would return -ESRCH by returning this error directly.
+
+[lorenzo.stoakes@oracle.com: prefer neater pointer error comparison]
+  Link: https://lkml.kernel.org/r/2fae1834-749a-45e1-8594-5e5979cf7103@lucifer.local
+Link: https://lkml.kernel.org/r/20240924201023.193135-1-lorenzo.stoakes@oracle.com
+Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+Suggested-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/proc/base.c         | 26 ++++++++++++++------------
+ kernel/fork.c          |  5 +++--
+ mm/madvise.c           |  4 ++--
+ mm/process_vm_access.c |  4 ++--
+ 4 files changed, 21 insertions(+), 18 deletions(-)
+
+diff --git a/fs/proc/base.c b/fs/proc/base.c
+index b89e6fbf23266c..6f24023b64f31e 100644
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -811,19 +811,21 @@ static const struct file_operations proc_single_file_operations = {
+ struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode)
+ {
+       struct task_struct *task = get_proc_task(inode);
+-      struct mm_struct *mm = ERR_PTR(-ESRCH);
++      struct mm_struct *mm;
+-      if (task) {
+-              mm = mm_access(task, mode | PTRACE_MODE_FSCREDS);
+-              put_task_struct(task);
++      if (!task)
++              return ERR_PTR(-ESRCH);
+-              if (!IS_ERR_OR_NULL(mm)) {
+-                      /* ensure this mm_struct can't be freed */
+-                      mmgrab(mm);
+-                      /* but do not pin its memory */
+-                      mmput(mm);
+-              }
+-      }
++      mm = mm_access(task, mode | PTRACE_MODE_FSCREDS);
++      put_task_struct(task);
++
++      if (IS_ERR(mm))
++              return mm == ERR_PTR(-ESRCH) ? NULL : mm;
++
++      /* ensure this mm_struct can't be freed */
++      mmgrab(mm);
++      /* but do not pin its memory */
++      mmput(mm);
+       return mm;
+ }
+@@ -2201,7 +2203,7 @@ static int map_files_d_revalidate(struct dentry *dentry, unsigned int flags)
+               goto out_notask;
+       mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+-      if (IS_ERR_OR_NULL(mm))
++      if (IS_ERR(mm))
+               goto out;
+       if (!dname_to_vma_addr(dentry, &vm_start, &vm_end)) {
+diff --git a/kernel/fork.c b/kernel/fork.c
+index db2a9016f636f4..63f56a292f756a 100644
+--- a/kernel/fork.c
++++ b/kernel/fork.c
+@@ -1418,8 +1418,9 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
+               return ERR_PTR(err);
+       mm = get_task_mm(task);
+-      if (mm && mm != current->mm &&
+-                      !ptrace_may_access(task, mode)) {
++      if (!mm) {
++              mm = ERR_PTR(-ESRCH);
++      } else if (mm != current->mm && !ptrace_may_access(task, mode)) {
+               mmput(mm);
+               mm = ERR_PTR(-EACCES);
+       }
+diff --git a/mm/madvise.c b/mm/madvise.c
+index 06c5adcaec5955..a3a3a423e9510c 100644
+--- a/mm/madvise.c
++++ b/mm/madvise.c
+@@ -1485,8 +1485,8 @@ SYSCALL_DEFINE5(process_madvise, int, pidfd, const struct iovec __user *, vec,
+       /* Require PTRACE_MODE_READ to avoid leaking ASLR metadata. */
+       mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+-      if (IS_ERR_OR_NULL(mm)) {
+-              ret = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH;
++      if (IS_ERR(mm)) {
++              ret = PTR_ERR(mm);
+               goto release_task;
+       }
+diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c
+index 78dfaf9e8990af..025cba94e63ff3 100644
+--- a/mm/process_vm_access.c
++++ b/mm/process_vm_access.c
+@@ -200,8 +200,8 @@ static ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter,
+       }
+       mm = mm_access(task, PTRACE_MODE_ATTACH_REALCREDS);
+-      if (!mm || IS_ERR(mm)) {
+-              rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH;
++      if (IS_ERR(mm)) {
++              rc = PTR_ERR(mm);
+               /*
+                * Explicitly map EACCES to EPERM as EPERM is a more
+                * appropriate error code for process_vw_readv/writev
+-- 
+2.53.0
+
index bd3fdd805f90252a7dfbaf673f7aa90a38ce49c6..e4555fae43e2dbb921409cdecb5f105914f6ec63 100644 (file)
@@ -21,3 +21,4 @@ gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
 xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
 input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
 input-ims-pcu-fix-logic-error-in-packet-reset.patch
+mm-refactor-mm_access-to-not-return-null.patch
diff --git a/queue-6.12/mm-refactor-mm_access-to-not-return-null.patch b/queue-6.12/mm-refactor-mm_access-to-not-return-null.patch
new file mode 100644 (file)
index 0000000..aa7bee6
--- /dev/null
@@ -0,0 +1,132 @@
+From 7e51752907c9bd2ed0203b7bec215834b16c11ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2024 21:10:23 +0100
+Subject: mm: refactor mm_access() to not return NULL
+
+From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+
+[ Upstream commit cd3f8467afd470ccab0de2fbc7c76664af4a0bac ]
+
+mm_access() can return NULL if the mm is not found, but this is handled
+the same as an error in all callers, with some translating this into an
+-ESRCH error.
+
+Only proc_mem_open() returns NULL if no mm is found, however in this case
+it is clearer and makes more sense to explicitly handle the error.
+Additionally we take the opportunity to refactor the function to eliminate
+unnecessary nesting.
+
+Simplify things by simply returning -ESRCH if no mm is found - this both
+eliminates confusing use of the IS_ERR_OR_NULL() macro, and simplifies
+callers which would return -ESRCH by returning this error directly.
+
+[lorenzo.stoakes@oracle.com: prefer neater pointer error comparison]
+  Link: https://lkml.kernel.org/r/2fae1834-749a-45e1-8594-5e5979cf7103@lucifer.local
+Link: https://lkml.kernel.org/r/20240924201023.193135-1-lorenzo.stoakes@oracle.com
+Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+Suggested-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/proc/base.c         | 26 ++++++++++++++------------
+ kernel/fork.c          |  5 +++--
+ mm/madvise.c           |  4 ++--
+ mm/process_vm_access.c |  4 ++--
+ 4 files changed, 21 insertions(+), 18 deletions(-)
+
+diff --git a/fs/proc/base.c b/fs/proc/base.c
+index 9f032236849783..427fb5abb07c7c 100644
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -817,19 +817,21 @@ static const struct file_operations proc_single_file_operations = {
+ struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode)
+ {
+       struct task_struct *task = get_proc_task(inode);
+-      struct mm_struct *mm = ERR_PTR(-ESRCH);
++      struct mm_struct *mm;
+-      if (task) {
+-              mm = mm_access(task, mode | PTRACE_MODE_FSCREDS);
+-              put_task_struct(task);
++      if (!task)
++              return ERR_PTR(-ESRCH);
+-              if (!IS_ERR_OR_NULL(mm)) {
+-                      /* ensure this mm_struct can't be freed */
+-                      mmgrab(mm);
+-                      /* but do not pin its memory */
+-                      mmput(mm);
+-              }
+-      }
++      mm = mm_access(task, mode | PTRACE_MODE_FSCREDS);
++      put_task_struct(task);
++
++      if (IS_ERR(mm))
++              return mm == ERR_PTR(-ESRCH) ? NULL : mm;
++
++      /* ensure this mm_struct can't be freed */
++      mmgrab(mm);
++      /* but do not pin its memory */
++      mmput(mm);
+       return mm;
+ }
+@@ -2199,7 +2201,7 @@ static int map_files_d_revalidate(struct dentry *dentry, unsigned int flags)
+               goto out_notask;
+       mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+-      if (IS_ERR_OR_NULL(mm))
++      if (IS_ERR(mm))
+               goto out;
+       if (!dname_to_vma_addr(dentry, &vm_start, &vm_end)) {
+diff --git a/kernel/fork.c b/kernel/fork.c
+index 6191f1f8bde86d..2b7577edffa2da 100644
+--- a/kernel/fork.c
++++ b/kernel/fork.c
+@@ -1580,8 +1580,9 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
+               return ERR_PTR(err);
+       mm = get_task_mm(task);
+-      if (mm && mm != current->mm &&
+-                      !ptrace_may_access(task, mode)) {
++      if (!mm) {
++              mm = ERR_PTR(-ESRCH);
++      } else if (mm != current->mm && !ptrace_may_access(task, mode)) {
+               mmput(mm);
+               mm = ERR_PTR(-EACCES);
+       }
+diff --git a/mm/madvise.c b/mm/madvise.c
+index 4a3b609a33e60d..d2b315993a1c01 100644
+--- a/mm/madvise.c
++++ b/mm/madvise.c
+@@ -1521,8 +1521,8 @@ SYSCALL_DEFINE5(process_madvise, int, pidfd, const struct iovec __user *, vec,
+       /* Require PTRACE_MODE_READ to avoid leaking ASLR metadata. */
+       mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+-      if (IS_ERR_OR_NULL(mm)) {
+-              ret = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH;
++      if (IS_ERR(mm)) {
++              ret = PTR_ERR(mm);
+               goto release_task;
+       }
+diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c
+index b308e96cd05a28..656d3e88755b6f 100644
+--- a/mm/process_vm_access.c
++++ b/mm/process_vm_access.c
+@@ -201,8 +201,8 @@ static ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter,
+       }
+       mm = mm_access(task, PTRACE_MODE_ATTACH_REALCREDS);
+-      if (!mm || IS_ERR(mm)) {
+-              rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH;
++      if (IS_ERR(mm)) {
++              rc = PTR_ERR(mm);
+               /*
+                * Explicitly map EACCES to EPERM as EPERM is a more
+                * appropriate error code for process_vw_readv/writev
+-- 
+2.53.0
+
diff --git a/queue-6.12/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch b/queue-6.12/posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
new file mode 100644 (file)
index 0000000..56080dc
--- /dev/null
@@ -0,0 +1,429 @@
+From cf7b5a3c34e508b49afdda31e7e32ab0d99c7ef9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 22:10:32 +0200
+Subject: posix-cpu-timers: Prevent UAF caused by non-leader exec() race
+
+From: Thomas Gleixner <tglx@kernel.org>
+
+commit 920f893f735e92ba3a1cd9256899a186b161928d upstream.
+
+Wongi and Jungwoo decoded and reported a non-leader exec() related race
+which can result in an UAF:
+
+ sys_timer_delete()                    exec()
+   posix_cpu_timer_del()
+   // Observes old leader
+   p = pid_task(pid, pid_type);                de_thread()
+                                         switch_leader();
+                                         release_task(old_leader)
+                                           __exit_signal(old_leader)
+                                             sighand = lock(old_leader, sighand);
+                                             posix_cpu_timers*_exit();
+   sighand = lock_task_sighand(p)            unhash_task(old_leader);
+     sh = lock(p, sighand)                   old_leader->sighand = NULL;
+                                             unlock(sighand);
+     (p->sighand == NULL)
+       unlock(sh)
+       return NULL;
+
+   // Returns without action
+   if(!sighand)
+      return 0;
+   free_posix_timer();
+
+This is "harmless" unless the deleted timer was armed and enqueued in
+p->signal because on exec() a TGID targeted timer is inherited.
+
+As sys_timer_delete() freed the underlying posix timer object
+run_posix_cpu_timers() or any timerqueue related add/delete operations on
+other timers will access the freed object's timerqueue node, which results
+in an UAF.
+
+There is a similar problem vs. posix_cpu_timer_set(). For regular posix
+timers it just transiently returns -ESRCH to user space, but for the use
+case in do_cpu_nanosleep() it's the same UAF just that the k_itimer is
+allocated on the stack.
+
+Also posix_cpu_timer_rearm() fails to rearm the timer, which means it stops
+to expire.
+
+While debating solutions Frederic pointed out another problem:
+
+   posix_cpu_timer_del(tmr)
+                                       __exit_signal(p)
+                                         posix_cpu_timers*_exit(p);
+                                         unhash_task(p);
+                                         p->sighand = NULL;
+     sh = lock_task_sighand(p)
+        sighand = p->sighand;
+       if (!sighand)
+           return NULL;
+       lock(sighand);
+
+     if (!sh)
+       WARN_ON_ONCE(timer_queued(tmr));
+
+On weakly ordered architectures it is not guaranteed that
+posix_cpu_timer_del() will observe the stores in posix_cpu_timers*_exit()
+when p->sighand is observed as NULL, which means the WARN() can be a false
+positive.
+
+Solve these issues by:
+
+  1) Changing the store in __exit_signal() to smp_store_release().
+
+  2) Adding a smp_acquire__after_ctrl_dep() into the !sighand path
+     of lock_task_sighand().
+
+  3) Creating a helper function for looking up the task and locking sighand
+     which does not return when sighand == NULL. Instead it retries the
+     task lookup and only if that fails it gives up.
+
+  4) Using that helper in the three affected functions.
+
+#1/#2 ensures that the reader side which observes sighand == NULL also
+observes all preceeding stores, i.e. the stores in posix_cpu_timers*_exit()
+and the ones in unhash_task().
+
+#3 ensures that the above described non-leader exec() situation is handled
+gracefully. When the task lookup returns the old leader, but sighand ==
+NULL then it retries. In the non-leader exec() case the subsequent task
+lookup will observe the new leader due to #1/#2. In normal exit() scenarios
+the subsequent lookup fails.
+
+When the task lookup fails, the function also checks whether the timer is
+still enqueued and issues a warning if that's the case. Unfortunately there
+is nothing which can be done about it, but as the task is already not
+longer visible the timer should not be accessed anymore. This check also
+requires memory ordering, which is not provided when the first lookup
+fails. To achieve that the check is preceeded by a smp_rmb() which pairs
+with the smp_wmb() in write_seqlock() in __exit_signal(). That ensures that
+the stores in posix_cpu_timers*_exit() are visible.
+
+The history of the non-leader exec() issue goes back to the early days of
+posix CPU timers, which stored a pointer to the group leader task in the
+timer. That obviously fails when a non-leader exec() switches the leader.
+commit e0a70217107e ("posix-cpu-timers: workaround to suppress the problems
+with mt exec") added a temporary workaround for that in 2010 which survived
+about 10 years. The fix for the workaround changed the task pointer to a
+pid pointer, but failed to see the subtle race described above. So the
+Fixes tag picks that commit, which seems to be halfways accurate.
+
+Thanks to Frederic Weissbecker, Oleg Nesterov and Peter Zijlstra for
+review, feedback and suggestions and to Wongi and Jungwoo for the excellent
+bug report and analysis!
+
+Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task")
+Reported-by: Wongi Lee <qw3rtyp0@gmail.com>
+Reported-by: Jungwoo Lee <jwlee2217@gmail.com>
+Signed-off-by: Thomas Gleixner <tglx@kernel.org>
+Reviewed-by: Oleg Nesterov <oleg@redhat.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ kernel/exit.c                  |   8 +-
+ kernel/signal.c                |  10 +-
+ kernel/time/posix-cpu-timers.c | 176 ++++++++++++++++++++++-----------
+ 3 files changed, 132 insertions(+), 62 deletions(-)
+
+diff --git a/kernel/exit.c b/kernel/exit.c
+index e798078f958c89..8ae45b2b8cf948 100644
+--- a/kernel/exit.c
++++ b/kernel/exit.c
+@@ -205,7 +205,13 @@ static void __exit_signal(struct task_struct *tsk)
+        * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
+        */
+       flush_sigqueue(&tsk->pending);
+-      tsk->sighand = NULL;
++
++      /*
++       * Ensure that all preceeding state is visible. Pairs with
++       * the smp_acquire__after_ctrl_dep() in the sighand == NULL
++       * path of lock_task_sighand().
++       */
++      smp_store_release(&tsk->sighand, NULL);
+       spin_unlock(&sighand->siglock);
+       __cleanup_sighand(sighand);
+diff --git a/kernel/signal.c b/kernel/signal.c
+index b832158a9c4608..e6e812f8b80ffc 100644
+--- a/kernel/signal.c
++++ b/kernel/signal.c
+@@ -1395,8 +1395,16 @@ struct sighand_struct *__lock_task_sighand(struct task_struct *tsk,
+       rcu_read_lock();
+       for (;;) {
+               sighand = rcu_dereference(tsk->sighand);
+-              if (unlikely(sighand == NULL))
++              if (unlikely(sighand == NULL)) {
++                      /*
++                       * Pairs with the smp_store_release() in
++                       * __exit_signal().  It ensures that all state
++                       * modifications to the task preceeding the store are
++                       * visible to the callers of lock_task_sighand().
++                       */
++                      smp_acquire__after_ctrl_dep();
+                       break;
++              }
+               /*
+                * This sighand can be already freed and even reused, but
+diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
+index 635fac5f5fbd58..f0471fbe87288b 100644
+--- a/kernel/time/posix-cpu-timers.c
++++ b/kernel/time/posix-cpu-timers.c
+@@ -462,6 +462,109 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
+               trigger_base_recalc_expires(timer, p);
+ }
++/*
++ * Lookup the task via timer->it.cpu.pid and attempt to lock the task's sighand.
++ *
++ * This can race with the reaping of the task:
++ *
++ * CPU0                                       CPU1
++ *
++ * // Finds task
++ * p = pid_task(pid, pid_type);               __exit_signal(p)
++ *                                      lock(p, sighand);
++ *                                      posix_cpu_timers*_exit();
++ * sighand = lock_task_sighand(p);      unhash_task(p);
++ *                                      p->sighand = NULL;
++ *                                      unlock(sighand);
++ *
++ * In this case sighand is NULL, which means the task and the associated timer
++ * queue cannot be longer accessed safely.
++ *
++ * __exit_signal() invokes posix_cpu_timers_exit() and if the thread group is
++ * dead it also invokes posix_cpu_timers_group_exit(). These functions delete
++ * all pending timers from the related timer queues. The POSIX timers (k_itimer)
++ * themself are still accessible, but not longer connected to the task.
++ *
++ * exec() works slightly differently. The task which exec()'s terminates all
++ * other threads in the thread group and runs __exit_signal() on them. As the
++ * thread group is not dead they only clean up the per task timers via
++ * posix_cpu_timers_exit().
++ *
++ * As the TGID on exec() stays the same per process timers stay queued, if they
++ * are armed. This works without a problem when exec() is done by the thread
++ * group leader. If a non-leader thread exec()'s this can end up in the
++ * following scenario:
++ *
++ * CPU0                                       CPU1
++ * // Returns old leader
++ * p = pid_task(pid, pid_type);               de_thread()
++ *                                    switch_leader()
++ *                                    release_task(old leader)
++ *                                      __exit_signal()
++ *                                      old_leader->sighand = NULL;
++ * // Returns NULL
++ * sighand = lock_task_sighand(p)
++ *
++ * That's problematic for several functions:
++ *
++ *  - posix_cpu_timer_del(): If the timer is still enqueued on the task the
++ *    underlying k_itimer will be freed which results in a UAF in
++ *    run_posix_cpu_timers() or on timerqueue related add/delete operations.
++ *    If the timer is not enqueued, the failure is harmless
++ *
++ *  - posix_cpu_timer_set(): Independent of the enqueued state that results in a
++ *    transient failure which is user space visible (-ESRCH) for regular posix
++ *    timers. But for the use case in do_cpu_nanosleep() it's the same UAF
++ *    problem just that the timer is allocated on the stack.
++ *
++ *  - posix_cpu_timer_rearm(): Timer is not enqueued at that point, but this
++ *    silently ignores the rearm request, which is a functional problem as the
++ *    timer wont expire anymore.
++ */
++static struct task_struct *timer_lock_sighand(struct k_itimer *timer, unsigned long *flags)
++{
++      enum pid_type type = clock_pid_type(timer->it_clock);
++      struct cpu_timer *ctmr = &timer->it.cpu;
++
++      guard(rcu)();
++
++      for (;;) {
++              struct task_struct *t = pid_task(timer->it.cpu.pid, type);
++
++              /* Fail if the task cannot be found. */
++              if (!t)
++                      break;
++
++              /* Try to lock the task's sighand */
++              if (lock_task_sighand(t, flags))
++                      return t;
++
++              /*
++               * The next PID lookup might either fail or return the new
++               * leader. This is correct for both exit() and exec().
++               */
++      }
++
++      /*
++       * If the timer is still enqueued, warn. There is nothing safe to do
++       * here as there might be two timers in there which are removed in
++       * parallel and that will cause more damage than good. This should never
++       * happen!
++       *
++       * Ensure that the stores to the timer and timerqueue are visible:
++       *
++       * __exit_signal()
++       *   posix_cpu_timers*_exit()
++       *   write_seqlock(seqlock)
++       *      smp_wmb(); <-------
++       *   __unhash_process()   |     !pid_task()
++       *                        ----> smp_rmb();
++       *                              WARN_ON_ONCE(...)
++       */
++      smp_rmb();
++      WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
++      return NULL;
++}
+ /*
+  * Clean up a CPU-clock timer that is about to be destroyed.
+@@ -471,29 +574,13 @@ static void disarm_timer(struct k_itimer *timer, struct task_struct *p)
+  */
+ static int posix_cpu_timer_del(struct k_itimer *timer)
+ {
+-      struct cpu_timer *ctmr = &timer->it.cpu;
+-      struct sighand_struct *sighand;
+       struct task_struct *p;
+       unsigned long flags;
+       int ret = 0;
+-      rcu_read_lock();
+-      p = cpu_timer_task_rcu(timer);
+-      if (!p)
+-              goto out;
++      p = timer_lock_sighand(timer, &flags);
+-      /*
+-       * Protect against sighand release/switch in exit/exec and process/
+-       * thread timer list entry concurrent read/writes.
+-       */
+-      sighand = lock_task_sighand(p, &flags);
+-      if (unlikely(sighand == NULL)) {
+-              /*
+-               * This raced with the reaping of the task. The exit cleanup
+-               * should have removed this timer from the timer queue.
+-               */
+-              WARN_ON_ONCE(ctmr->head || timerqueue_node_queued(&ctmr->node));
+-      } else {
++      if (likely(p)) {
+               if (timer->it.cpu.firing)
+                       ret = TIMER_RETRY;
+               else
+@@ -502,10 +589,8 @@ static int posix_cpu_timer_del(struct k_itimer *timer)
+               unlock_task_sighand(p, &flags);
+       }
+-out:
+-      rcu_read_unlock();
+       if (!ret)
+-              put_pid(ctmr->pid);
++              put_pid(timer->it.cpu.pid);
+       return ret;
+ }
+@@ -627,21 +712,17 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
+       clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
+       struct cpu_timer *ctmr = &timer->it.cpu;
+       u64 old_expires, new_expires, now;
+-      struct sighand_struct *sighand;
+       struct task_struct *p;
+       unsigned long flags;
+       int ret = 0;
+-      rcu_read_lock();
+-      p = cpu_timer_task_rcu(timer);
+-      if (!p) {
+-              /*
+-               * If p has just been reaped, we can no
+-               * longer get any information about it at all.
+-               */
+-              rcu_read_unlock();
++      p = timer_lock_sighand(timer, &flags);
++      /*
++       * If p has just been reaped, we can no longer get any information about
++       * it at all.
++       */
++      if (!p)
+               return -ESRCH;
+-      }
+       /*
+        * Use the to_ktime conversion because that clamps the maximum
+@@ -649,20 +730,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
+        */
+       new_expires = ktime_to_ns(timespec64_to_ktime(new->it_value));
+-      /*
+-       * Protect against sighand release/switch in exit/exec and p->cpu_timers
+-       * and p->signal->cpu_timers read/write in arm_timer()
+-       */
+-      sighand = lock_task_sighand(p, &flags);
+-      /*
+-       * If p has just been reaped, we can no
+-       * longer get any information about it at all.
+-       */
+-      if (unlikely(sighand == NULL)) {
+-              rcu_read_unlock();
+-              return -ESRCH;
+-      }
+-
+       /* Retrieve the current expiry time before disarming the timer */
+       old_expires = cpu_timer_getexpires(ctmr);
+@@ -693,7 +760,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
+       /* Retry if the timer expiry is running concurrently */
+       if (unlikely(ret)) {
+               unlock_task_sighand(p, &flags);
+-              goto out;
++              return ret;
+       }
+       /* Convert relative expiry time to absolute */
+@@ -728,8 +795,6 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags,
+        */
+       if (!sigev_none && new_expires && now >= new_expires)
+               cpu_timer_fire(timer);
+-out:
+-      rcu_read_unlock();
+       return ret;
+ }
+@@ -1011,19 +1076,12 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
+ {
+       clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock);
+       struct task_struct *p;
+-      struct sighand_struct *sighand;
+       unsigned long flags;
+       u64 now;
+-      rcu_read_lock();
+-      p = cpu_timer_task_rcu(timer);
+-      if (!p)
+-              goto out;
+-
+-      /* Protect timer list r/w in arm_timer() */
+-      sighand = lock_task_sighand(p, &flags);
+-      if (unlikely(sighand == NULL))
+-              goto out;
++      p = timer_lock_sighand(timer, &flags);
++      if (unlikely(!p))
++              return;
+       /*
+        * Fetch the current sample and update the timer's expiry time.
+@@ -1040,8 +1098,6 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer)
+        */
+       arm_timer(timer, p);
+       unlock_task_sighand(p, &flags);
+-out:
+-      rcu_read_unlock();
+ }
+ /**
+-- 
+2.53.0
+
index f8f3aa0e24a28d1813a4b7cac2e42dc2f9d73b8c..385379a618326db545e98804dee1c12640ac9907 100644 (file)
@@ -33,3 +33,5 @@ xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
 input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
 input-ims-pcu-fix-logic-error-in-packet-reset.patch
 soc-qcom-ice-allow-explicit-votes-on-iface-clock-for.patch
+mm-refactor-mm_access-to-not-return-null.patch
+posix-cpu-timers-prevent-uaf-caused-by-non-leader-ex.patch
diff --git a/queue-6.18/fuse-fix-writeback-array-overflow-when-max_pages-is-.patch b/queue-6.18/fuse-fix-writeback-array-overflow-when-max_pages-is-.patch
new file mode 100644 (file)
index 0000000..4ffda8f
--- /dev/null
@@ -0,0 +1,56 @@
+From 857a53c685e453c0dad7c6bb50418a597b759fe1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 25 Jul 2026 17:04:08 +0800
+Subject: fuse: fix writeback array overflow when max_pages is one
+
+From: Junxi Qian <qjx1298677004@gmail.com>
+
+[ Upstream commit c3880a7b10e487e033dc6f388bda118436566f7a ]
+
+fuse_iomap_writeback_range() appends one folio pointer and one
+fuse_folio_desc for every dirty range that is merged into the current
+writeback request.  The merge decision checks the byte budget against
+fc->max_pages and fc->max_write, but it does not check whether the folio
+and descriptor arrays still have another free slot.
+
+This is not sufficient for fuseblk, where the filesystem block size can
+be smaller than PAGE_SIZE.  With writeback cache enabled and max_pages
+negotiated as one, contiguous sub-page dirty ranges can fit within the
+byte budget while spanning more than one folio.  The next append can then
+write past the one-slot folios and descs arrays.
+
+Split the request when the number of already attached folios has reached
+fc->max_pages.  This keeps the folio/descriptor slot accounting in sync
+with the send decision.
+
+Fixes: ef7e7cbb323f ("fuse: use iomap for writeback")
+Cc: stable@vger.kernel.org
+Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
+Signed-off-by: Junxi Qian <qjx1298677004@gmail.com>
+Link: https://patch.msgid.link/20260506122415.205340-1-qjx1298677004@gmail.com
+Acked-by: Miklos Szeredi <mszeredi@redhat.com>
+Signed-off-by: Christian Brauner <brauner@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/fuse/file.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/fs/fuse/file.c b/fs/fuse/file.c
+index 79d1b502e73195..585dd90361b654 100644
+--- a/fs/fuse/file.c
++++ b/fs/fuse/file.c
+@@ -2140,7 +2140,10 @@ static bool fuse_writepage_need_send(struct fuse_conn *fc, loff_t pos,
+       WARN_ON(!ap->num_folios);
+-      /* Reached max pages */
++      /* Reached max pages or max folio slots */
++      if (ap->num_folios >= fc->max_pages)
++              return true;
++
+       if ((bytes + PAGE_SIZE - 1) >> PAGE_SHIFT > fc->max_pages)
+               return true;
+-- 
+2.53.0
+
index da7a5c9889e7f3b5acb8e93446b4cced1d0dddca..05c7a67a5a3e671b95557e671bcfd1d73916d5a7 100644 (file)
@@ -29,3 +29,4 @@ crypto-tegra-don-t-touch-bo-refcount-in-host1x-bo-pi.patch
 xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
 input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
 input-ims-pcu-fix-logic-error-in-packet-reset.patch
+fuse-fix-writeback-array-overflow-when-max_pages-is-.patch
diff --git a/queue-6.6/mm-refactor-mm_access-to-not-return-null.patch b/queue-6.6/mm-refactor-mm_access-to-not-return-null.patch
new file mode 100644 (file)
index 0000000..88bdb68
--- /dev/null
@@ -0,0 +1,132 @@
+From f99ae878d27857cf473a19743d6a59f22b22506e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Sep 2024 21:10:23 +0100
+Subject: mm: refactor mm_access() to not return NULL
+
+From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+
+[ Upstream commit cd3f8467afd470ccab0de2fbc7c76664af4a0bac ]
+
+mm_access() can return NULL if the mm is not found, but this is handled
+the same as an error in all callers, with some translating this into an
+-ESRCH error.
+
+Only proc_mem_open() returns NULL if no mm is found, however in this case
+it is clearer and makes more sense to explicitly handle the error.
+Additionally we take the opportunity to refactor the function to eliminate
+unnecessary nesting.
+
+Simplify things by simply returning -ESRCH if no mm is found - this both
+eliminates confusing use of the IS_ERR_OR_NULL() macro, and simplifies
+callers which would return -ESRCH by returning this error directly.
+
+[lorenzo.stoakes@oracle.com: prefer neater pointer error comparison]
+  Link: https://lkml.kernel.org/r/2fae1834-749a-45e1-8594-5e5979cf7103@lucifer.local
+Link: https://lkml.kernel.org/r/20240924201023.193135-1-lorenzo.stoakes@oracle.com
+Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
+Suggested-by: Arnd Bergmann <arnd@arndb.de>
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/proc/base.c         | 26 ++++++++++++++------------
+ kernel/fork.c          |  5 +++--
+ mm/madvise.c           |  4 ++--
+ mm/process_vm_access.c |  4 ++--
+ 4 files changed, 21 insertions(+), 18 deletions(-)
+
+diff --git a/fs/proc/base.c b/fs/proc/base.c
+index da5c436ea36fd6..881c2d4846d7ce 100644
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -811,19 +811,21 @@ static const struct file_operations proc_single_file_operations = {
+ struct mm_struct *proc_mem_open(struct inode *inode, unsigned int mode)
+ {
+       struct task_struct *task = get_proc_task(inode);
+-      struct mm_struct *mm = ERR_PTR(-ESRCH);
++      struct mm_struct *mm;
+-      if (task) {
+-              mm = mm_access(task, mode | PTRACE_MODE_FSCREDS);
+-              put_task_struct(task);
++      if (!task)
++              return ERR_PTR(-ESRCH);
+-              if (!IS_ERR_OR_NULL(mm)) {
+-                      /* ensure this mm_struct can't be freed */
+-                      mmgrab(mm);
+-                      /* but do not pin its memory */
+-                      mmput(mm);
+-              }
+-      }
++      mm = mm_access(task, mode | PTRACE_MODE_FSCREDS);
++      put_task_struct(task);
++
++      if (IS_ERR(mm))
++              return mm == ERR_PTR(-ESRCH) ? NULL : mm;
++
++      /* ensure this mm_struct can't be freed */
++      mmgrab(mm);
++      /* but do not pin its memory */
++      mmput(mm);
+       return mm;
+ }
+@@ -2201,7 +2203,7 @@ static int map_files_d_revalidate(struct dentry *dentry, unsigned int flags)
+               goto out_notask;
+       mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+-      if (IS_ERR_OR_NULL(mm))
++      if (IS_ERR(mm))
+               goto out;
+       if (!dname_to_vma_addr(dentry, &vm_start, &vm_end)) {
+diff --git a/kernel/fork.c b/kernel/fork.c
+index 724040ac589501..36854aaec482d6 100644
+--- a/kernel/fork.c
++++ b/kernel/fork.c
+@@ -1570,8 +1570,9 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
+               return ERR_PTR(err);
+       mm = get_task_mm(task);
+-      if (mm && mm != current->mm &&
+-                      !ptrace_may_access(task, mode)) {
++      if (!mm) {
++              mm = ERR_PTR(-ESRCH);
++      } else if (mm != current->mm && !ptrace_may_access(task, mode)) {
+               mmput(mm);
+               mm = ERR_PTR(-EACCES);
+       }
+diff --git a/mm/madvise.c b/mm/madvise.c
+index 73ea053c9003ab..0c30710bfd859f 100644
+--- a/mm/madvise.c
++++ b/mm/madvise.c
+@@ -1492,8 +1492,8 @@ SYSCALL_DEFINE5(process_madvise, int, pidfd, const struct iovec __user *, vec,
+       /* Require PTRACE_MODE_READ to avoid leaking ASLR metadata. */
+       mm = mm_access(task, PTRACE_MODE_READ_FSCREDS);
+-      if (IS_ERR_OR_NULL(mm)) {
+-              ret = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH;
++      if (IS_ERR(mm)) {
++              ret = PTR_ERR(mm);
+               goto release_task;
+       }
+diff --git a/mm/process_vm_access.c b/mm/process_vm_access.c
+index 0523edab03a6a5..5be8e91aa87209 100644
+--- a/mm/process_vm_access.c
++++ b/mm/process_vm_access.c
+@@ -200,8 +200,8 @@ static ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter,
+       }
+       mm = mm_access(task, PTRACE_MODE_ATTACH_REALCREDS);
+-      if (!mm || IS_ERR(mm)) {
+-              rc = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH;
++      if (IS_ERR(mm)) {
++              rc = PTR_ERR(mm);
+               /*
+                * Explicitly map EACCES to EPERM as EPERM is a more
+                * appropriate error code for process_vw_readv/writev
+-- 
+2.53.0
+
index 008cb2be1309346a22cc1f5dcc6be9f2df38f997..f1b0ecf287fb8ec4364b6056c5e816b350bb684f 100644 (file)
@@ -26,3 +26,4 @@ gpu-host1x-fix-use-after-free-in-host1x_bo_clear_cac.patch
 xprtrdma-clear-receive-side-ownership-pointers-on-re.patch
 input-ims-pcu-fix-heap-buffer-overflow-in-ims_pcu_pr.patch
 input-ims-pcu-fix-logic-error-in-packet-reset.patch
+mm-refactor-mm_access-to-not-return-null.patch