From nobody Mon Feb 9 14:02:48 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 05C73C001DC for ; Tue, 11 Jul 2023 16:00:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233258AbjGKQAK (ORCPT ); Tue, 11 Jul 2023 12:00:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37606 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233263AbjGKQAF (ORCPT ); Tue, 11 Jul 2023 12:00:05 -0400 Received: from pegase1.c-s.fr (pegase1.c-s.fr [93.17.236.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2CABB1710 for ; Tue, 11 Jul 2023 08:59:56 -0700 (PDT) Received: from localhost (mailhub3.si.c-s.fr [192.168.12.233]) by localhost (Postfix) with ESMTP id 4R0ltb1Wzqz9sFM; Tue, 11 Jul 2023 17:59:35 +0200 (CEST) X-Virus-Scanned: amavisd-new at c-s.fr Received: from pegase1.c-s.fr ([192.168.12.234]) by localhost (pegase1.c-s.fr [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id k_gPEhSYbzxo; Tue, 11 Jul 2023 17:59:35 +0200 (CEST) Received: from messagerie.si.c-s.fr (messagerie.si.c-s.fr [192.168.25.192]) by pegase1.c-s.fr (Postfix) with ESMTP id 4R0ltZ39TLz9sFF; Tue, 11 Jul 2023 17:59:34 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by messagerie.si.c-s.fr (Postfix) with ESMTP id 6963D8B77B; Tue, 11 Jul 2023 17:59:34 +0200 (CEST) X-Virus-Scanned: amavisd-new at c-s.fr Received: from messagerie.si.c-s.fr ([127.0.0.1]) by localhost (messagerie.si.c-s.fr [127.0.0.1]) (amavisd-new, port 10023) with ESMTP id svtvSD4nAahM; Tue, 11 Jul 2023 17:59:34 +0200 (CEST) Received: from PO20335.IDSI0.si.c-s.fr (unknown [192.168.233.184]) by messagerie.si.c-s.fr (Postfix) with ESMTP id CAB218B779; Tue, 11 Jul 2023 17:59:33 +0200 (CEST) Received: from PO20335.IDSI0.si.c-s.fr (localhost [127.0.0.1]) by PO20335.IDSI0.si.c-s.fr (8.17.1/8.16.1) with ESMTPS id 36BFxVdE3695867 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 11 Jul 2023 17:59:31 +0200 Received: (from chleroy@localhost) by PO20335.IDSI0.si.c-s.fr (8.17.1/8.17.1/Submit) id 36BFxV2u3695866; Tue, 11 Jul 2023 17:59:31 +0200 X-Authentication-Warning: PO20335.IDSI0.si.c-s.fr: chleroy set sender to christophe.leroy@csgroup.eu using -f From: Christophe Leroy To: Michael Ellerman , Nicholas Piggin Cc: Christophe Leroy , linux-kernel@vger.kernel.org, linuxppc-dev@lists.ozlabs.org Subject: [PATCH v3 7/9] powerpc/kuap: Simplify KUAP lock/unlock on BOOK3S/32 Date: Tue, 11 Jul 2023 17:59:19 +0200 Message-ID: <8de8580513c1a6e880bad1ba9a69d3efad3d4fa5.1689091022.git.christophe.leroy@csgroup.eu> X-Mailer: git-send-email 2.41.0 In-Reply-To: References: MIME-Version: 1.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1689091151; l=7191; i=christophe.leroy@csgroup.eu; s=20211009; h=from:subject:message-id; bh=ZQrzF847yISI+GzGtwHQSW/yEnyJaXLj7oiMAtgcy1U=; b=ufIM/2GreoRLTrTEioVhDZS6a0RxANy0LEH8uipqKil5xUISlqnK+VLfm52zMv/MfSVnTA6Km Ub4sttfzcQDCvMeeP7o9KaJyDO+/oZVLH+z0nz8fzUUIXH6Y7HPMGxP X-Developer-Key: i=christophe.leroy@csgroup.eu; a=ed25519; pk=HIzTzUj91asvincQGOFx6+ZF5AoUuP9GdOtQChs7Mm0= Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" On book3s/32 KUAP is performed at segment level. At the moment, when enabling userspace access, only current segment is modified. Then if a write is performed on another user segment, a fault is taken and all other user segments get enabled for userspace access. This then require special attention when disabling userspace access. Having a userspace write access crossing a segment boundary is unlikely. Having a userspace write access crossing a segment boundary back and forth is even more unlikely. So, instead of enabling userspace access on all segments when a write fault occurs, just change which segment has userspace access enabled in order to eliminate the case when more than one segment has userspace access enabled. That simplifies userspace access deactivation. There is however a corner case which is even more unlikely but has to be handled anyway: an unaligned access which is crossing a segment boundary. That would definitely require at least having userspace access enabled on the two segments. To avoid complicating the likely case for a so unlikely happening, handle such situation like an alignment exception and emulate the store. Signed-off-by: Christophe Leroy --- arch/powerpc/include/asm/book3s/32/kup.h | 65 +++++++----------------- arch/powerpc/include/asm/bug.h | 1 + arch/powerpc/kernel/traps.c | 2 +- arch/powerpc/mm/book3s32/kuap.c | 15 +----- 4 files changed, 23 insertions(+), 60 deletions(-) diff --git a/arch/powerpc/include/asm/book3s/32/kup.h b/arch/powerpc/includ= e/asm/book3s/32/kup.h index 4ca6122ef0e1..452d4efa84f5 100644 --- a/arch/powerpc/include/asm/book3s/32/kup.h +++ b/arch/powerpc/include/asm/book3s/32/kup.h @@ -14,7 +14,6 @@ #include =20 #define KUAP_NONE (~0UL) -#define KUAP_ALL (~1UL) =20 static inline void kuap_lock_one(unsigned long addr) { @@ -28,41 +27,6 @@ static inline void kuap_unlock_one(unsigned long addr) isync(); /* Context sync required after mtsr() */ } =20 -static inline void kuap_lock_all(void) -{ - update_user_segments(mfsr(0) | SR_KS); - isync(); /* Context sync required after mtsr() */ -} - -static inline void kuap_unlock_all(void) -{ - update_user_segments(mfsr(0) & ~SR_KS); - isync(); /* Context sync required after mtsr() */ -} - -void kuap_lock_all_ool(void); -void kuap_unlock_all_ool(void); - -static inline void kuap_lock_addr(unsigned long addr, bool ool) -{ - if (likely(addr !=3D KUAP_ALL)) - kuap_lock_one(addr); - else if (!ool) - kuap_lock_all(); - else - kuap_lock_all_ool(); -} - -static inline void kuap_unlock(unsigned long addr, bool ool) -{ - if (likely(addr !=3D KUAP_ALL)) - kuap_unlock_one(addr); - else if (!ool) - kuap_unlock_all(); - else - kuap_unlock_all_ool(); -} - static inline void __kuap_save_and_lock(struct pt_regs *regs) { unsigned long kuap =3D current->thread.kuap; @@ -72,7 +36,7 @@ static inline void __kuap_save_and_lock(struct pt_regs *r= egs) return; =20 current->thread.kuap =3D KUAP_NONE; - kuap_lock_addr(kuap, false); + kuap_lock_one(kuap); } #define __kuap_save_and_lock __kuap_save_and_lock =20 @@ -84,7 +48,7 @@ static inline void __kuap_kernel_restore(struct pt_regs *= regs, unsigned long kua { if (unlikely(kuap !=3D KUAP_NONE)) { current->thread.kuap =3D KUAP_NONE; - kuap_lock_addr(kuap, false); + kuap_lock_one(kuap); } =20 if (likely(regs->kuap =3D=3D KUAP_NONE)) @@ -92,7 +56,7 @@ static inline void __kuap_kernel_restore(struct pt_regs *= regs, unsigned long kua =20 current->thread.kuap =3D regs->kuap; =20 - kuap_unlock(regs->kuap, false); + kuap_unlock_one(regs->kuap); } =20 static inline unsigned long __kuap_get_and_assert_locked(void) @@ -127,7 +91,7 @@ static __always_inline void __prevent_user_access(unsign= ed long dir) return; =20 current->thread.kuap =3D KUAP_NONE; - kuap_lock_addr(kuap, true); + kuap_lock_one(kuap); } =20 static inline unsigned long __prevent_user_access_return(void) @@ -136,7 +100,7 @@ static inline unsigned long __prevent_user_access_retur= n(void) =20 if (flags !=3D KUAP_NONE) { current->thread.kuap =3D KUAP_NONE; - kuap_lock_addr(flags, true); + kuap_lock_one(flags); } =20 return flags; @@ -146,7 +110,7 @@ static inline void __restore_user_access(unsigned long = flags) { if (flags !=3D KUAP_NONE) { current->thread.kuap =3D flags; - kuap_unlock(flags, true); + kuap_unlock_one(flags); } } =20 @@ -155,14 +119,23 @@ __bad_kuap_fault(struct pt_regs *regs, unsigned long = address, bool is_write) { unsigned long kuap =3D regs->kuap; =20 - if (!is_write || kuap =3D=3D KUAP_ALL) + if (!is_write) return false; if (kuap =3D=3D KUAP_NONE) return true; =20 - /* If faulting address doesn't match unlocked segment, unlock all */ - if ((kuap ^ address) & 0xf0000000) - regs->kuap =3D KUAP_ALL; + /* + * If faulting address doesn't match unlocked segment, change segment. + * In case of unaligned store crossing two segments, emulate store. + */ + if ((kuap ^ address) & 0xf0000000) { + if (!(kuap & 0x0fffffff) && address > kuap - 4 && fix_alignment(regs)) { + regs_add_return_ip(regs, 4); + emulate_single_step(regs); + } else { + regs->kuap =3D address; + } + } =20 return false; } diff --git a/arch/powerpc/include/asm/bug.h b/arch/powerpc/include/asm/bug.h index ef42adb44aa3..492530adecc2 100644 --- a/arch/powerpc/include/asm/bug.h +++ b/arch/powerpc/include/asm/bug.h @@ -163,6 +163,7 @@ __label_warn_on: \ struct pt_regs; void hash__do_page_fault(struct pt_regs *); void bad_page_fault(struct pt_regs *, int); +void emulate_single_step(struct pt_regs *regs); extern void _exception(int, struct pt_regs *, int, unsigned long); extern void _exception_pkey(struct pt_regs *, unsigned long, int); extern void die(const char *, struct pt_regs *, long); diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index e59ec6d32d37..ab95105c69ca 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -1158,7 +1158,7 @@ DEFINE_INTERRUPT_HANDLER(single_step_exception) * pretend we got a single-step exception. This was pointed out * by Kumar Gala. -- paulus */ -static void emulate_single_step(struct pt_regs *regs) +void emulate_single_step(struct pt_regs *regs) { if (single_stepping(regs)) __single_step_exception(regs); diff --git a/arch/powerpc/mm/book3s32/kuap.c b/arch/powerpc/mm/book3s32/kua= p.c index 24c1c686e6b9..3a8815555a48 100644 --- a/arch/powerpc/mm/book3s32/kuap.c +++ b/arch/powerpc/mm/book3s32/kuap.c @@ -3,22 +3,11 @@ #include #include =20 -void kuap_lock_all_ool(void) -{ - kuap_lock_all(); -} -EXPORT_SYMBOL(kuap_lock_all_ool); - -void kuap_unlock_all_ool(void) -{ - kuap_unlock_all(); -} -EXPORT_SYMBOL(kuap_unlock_all_ool); - void setup_kuap(bool disabled) { if (!disabled) { - kuap_lock_all_ool(); + update_user_segments(mfsr(0) | SR_KS); + isync(); /* Context sync required after mtsr() */ init_mm.context.sr0 |=3D SR_KS; current->thread.sr0 |=3D SR_KS; } --=20 2.41.0