From a8512f5ea70a77a6a2f4d7e9124183b05dcd21b4 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 8 Apr 2026 13:18:57 -0700 Subject: [PATCH 1/2] x86: shadow stacks: proper error handling for mmap lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #35989 commit c79cf42321600e931933e11f94aba8b245d4cd66 stable. commit 52f657e34d7b21b47434d9d8b26fa7f6778b63a0 upstream. [ Upstream commit 52f657e34d7b21b47434d9d8b26fa7f6778b63a0 ] 김영민 reports that shstk_pop_sigframe() doesn't check for errors from mmap_read_lock_killable(), which is a silly oversight, and also shows that we haven't marked those functions with "__must_check", which would have immediately caught it. So let's fix both issues. Reported-by: 김영민 Acked-by: Oleg Nesterov Acked-by: Dave Hansen Acked-by: Rick Edgecombe Signed-off-by: Linus Torvalds Signed-off-by: Sasha Levin [Fixes conflicts by Kbackport] Assisted-by: PatchPilot Signed-off-by: Dust Li --- arch/x86/kernel/shstk.c | 3 ++- include/linux/mmap_lock.h | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c index 19e4db582fb6..d259d7d5b962 100644 --- a/arch/x86/kernel/shstk.c +++ b/arch/x86/kernel/shstk.c @@ -311,7 +311,8 @@ static int shstk_pop_sigframe(unsigned long *ssp) need_to_check_vma = PAGE_ALIGN(*ssp) == *ssp; if (need_to_check_vma) - mmap_read_lock_killable(current->mm); + if (mmap_read_lock_killable(current->mm)) + return -EINTR; err = get_shstk_data(&token_addr, (unsigned long __user *)*ssp); if (unlikely(err)) diff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h index 02387188bad2..21dac6a4892c 100644 --- a/include/linux/mmap_lock.h +++ b/include/linux/mmap_lock.h @@ -116,7 +116,7 @@ static inline void mmap_write_lock_nested(struct mm_struct *mm, int subclass) __mmap_lock_trace_acquire_returned(mm, true, true); } -static inline int mmap_write_lock_killable(struct mm_struct *mm) +static inline int __must_check mmap_write_lock_killable(struct mm_struct *mm) { int ret; @@ -164,7 +164,7 @@ static inline void mmap_read_lock_nested(struct mm_struct *mm, int subclass) __mmap_lock_trace_acquire_returned(mm, true, true); } -static inline int mmap_read_lock_killable(struct mm_struct *mm) +static inline int __must_check mmap_read_lock_killable(struct mm_struct *mm) { int ret; @@ -174,7 +174,7 @@ static inline int mmap_read_lock_killable(struct mm_struct *mm) return ret; } -static inline bool mmap_read_trylock(struct mm_struct *mm) +static inline bool __must_check mmap_read_trylock(struct mm_struct *mm) { bool ret; -- Gitee From 319dd75961d137788f8f586fc65072b3a33fed78 Mon Sep 17 00:00:00 2001 From: Rick Edgecombe Date: Thu, 7 May 2026 16:53:09 -0700 Subject: [PATCH 2/2] x86/shstk: Prevent deadlock during shstk sigreturn ANBZ: #35989 commit e2c2b044458cbf22da05264fa707308e8d4f86f9 stable. commit 9874b2917b9fbc30956fee209d3c4aa47201c64e upstream. [ Upstream commit 9874b2917b9fbc30956fee209d3c4aa47201c64e ] During sigreturn the shadow stack signal frame is popped. The kernel does this by reading the shadow stack using normal read accesses. When it can't assume the memory is shadow stack, it takes extra steps to makes sure it is reading actual shadow stack memory and not other normal readable memory. It does this by holding the mmap read lock while doing the access and checking the flags of the VMA. Unfortunately that is not safe. If the read of the shadow stack sigframe hits a page fault, the fault handler will try to recursively grab another mmap read lock. This normally works ok, but if a writer on another CPU is also waiting, the second read lock could fail and cause a deadlock. Fix this by doing the read of the userspace memory via gup. Embed it in the get_shstk_data() helper. Currently there is a check that skips the lookup work when the SSP can be assumed to be on a shadow stack. While reorganizing the function, remove the optimization to make the tricky code flows more common, such that issues like this cannot escape detection for so long. [Due to missing per-vma MM sequence counter, use a simpler GUP based solution for the backport] Cc: # Depends on https://lore.kernel.org/all/20260504205924.536382-1-rick.p.edgecombe@intel.com/ Signed-off-by: Rick Edgecombe Signed-off-by: Sasha Levin [Fixes conflicts by Kbackport] Assisted-by: PatchPilot Signed-off-by: Dust Li --- arch/x86/kernel/shstk.c | 46 ++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/arch/x86/kernel/shstk.c b/arch/x86/kernel/shstk.c index d259d7d5b962..ba93c4e6a231 100644 --- a/arch/x86/kernel/shstk.c +++ b/arch/x86/kernel/shstk.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -262,11 +263,29 @@ static int put_shstk_data(u64 __user *addr, u64 data) return 0; } +/* Copy from aligned address in userspace without risk of page fault. */ +static int shstk_copy_user_gup(unsigned long *ldata, unsigned long __user *addr) +{ + struct page *page; + void *kaddr; + + mmap_assert_locked(current->mm); + if (get_user_pages((unsigned long)addr, 1, 0, &page) != 1) + return -EFAULT; + + kaddr = kmap_local_page(page); + *ldata = *(unsigned long *)(kaddr + offset_in_page(addr)); + kunmap_local(kaddr); + put_page(page); + + return 0; +} + static int get_shstk_data(unsigned long *data, unsigned long __user *addr) { unsigned long ldata; - if (unlikely(get_user(ldata, addr))) + if (shstk_copy_user_gup(&ldata, addr)) return -EFAULT; if (!(ldata & SHSTK_DATA_BIT)) @@ -296,7 +315,6 @@ static int shstk_pop_sigframe(unsigned long *ssp) { struct vm_area_struct *vma; unsigned long token_addr; - bool need_to_check_vma; int err = 1; /* @@ -308,26 +326,21 @@ static int shstk_pop_sigframe(unsigned long *ssp) if (!IS_ALIGNED(*ssp, 8)) return -EINVAL; - need_to_check_vma = PAGE_ALIGN(*ssp) == *ssp; - - if (need_to_check_vma) - if (mmap_read_lock_killable(current->mm)) - return -EINTR; + if (mmap_read_lock_killable(current->mm)) + return -EINTR; err = get_shstk_data(&token_addr, (unsigned long __user *)*ssp); if (unlikely(err)) goto out_err; - if (need_to_check_vma) { - vma = find_vma(current->mm, *ssp); - if (!vma || !(vma->vm_flags & VM_SHADOW_STACK)) { - err = -EFAULT; - goto out_err; - } - - mmap_read_unlock(current->mm); + vma = find_vma(current->mm, *ssp); + if (!vma || !(vma->vm_flags & VM_SHADOW_STACK)) { + err = -EFAULT; + goto out_err; } + mmap_read_unlock(current->mm); + /* Restore SSP aligned? */ if (unlikely(!IS_ALIGNED(token_addr, 8))) return -EINVAL; @@ -340,8 +353,7 @@ static int shstk_pop_sigframe(unsigned long *ssp) return 0; out_err: - if (need_to_check_vma) - mmap_read_unlock(current->mm); + mmap_read_unlock(current->mm); return err; } -- Gitee