From patchwork Tue Aug 30 09:16:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vladimir Isaev X-Patchwork-Id: 12958954 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 bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3B187ECAAA1 for ; Tue, 30 Aug 2022 09:17:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:Cc :To:From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=qcxH6bI4BjId8eVKeYxkfaB9xJWNl85NPtbSS6oDbwM=; b=gAUW6ud2F6owjd 2x2Aczf8MADKcSS+dhSmBXMdlQcIZQwVC9v5zo39wrnb2xGLwgY+1FJDPP5NBMLZi6FBf3OdKicVj DYxp744Raonnu41fmHp+oQwpnazmltCSayVUyEefUMpAnsjHIGNF1t8wfh7rjZjtOBsgJmO2mmHLR xoE2jrX5Hjs2YpisYluNa1R5B4aE6AM//EWzJvLpDWetZEnqApSNHayW4hOOebV00owGqkDY3pWmm 3lwRK84rBSd1Iw0IPSRSRO17EvDSfXjecUFPxvLQI96asUj2h1Wm2I3sK/FAdwGm5MXOENiJk7XFp Ox1n94qGxtOGAdAaR5pw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1oSxNI-00FXVf-45; Tue, 30 Aug 2022 09:17:36 +0000 Received: from forward108o.mail.yandex.net ([2a02:6b8:0:1a2d::206]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1oSxNC-00FXPq-Jx for linux-riscv@lists.infradead.org; Tue, 30 Aug 2022 09:17:35 +0000 Received: from iva1-adaa4d2a0364.qloud-c.yandex.net (iva1-adaa4d2a0364.qloud-c.yandex.net [IPv6:2a02:6b8:c0c:a0e:0:640:adaa:4d2a]) by forward108o.mail.yandex.net (Yandex) with ESMTP id 4068C5DD7EA4; Tue, 30 Aug 2022 12:17:25 +0300 (MSK) Received: by iva1-adaa4d2a0364.qloud-c.yandex.net (smtp/Yandex) with ESMTPSA id Rcl7ngY25D-HMhCatJG; Tue, 30 Aug 2022 12:17:24 +0300 (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (Client certificate not present) X-Yandex-Fwd: 1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=syntacore.com; s=mail; t=1661851044; bh=B4a1w+8X1uRqXIZomjyapuGz81XQ66nWGsIdhkmfuHg=; h=Message-Id:Date:Cc:Subject:To:From; b=hAOgtDsrFYH1Te6imtMFSiWE3AANj8suZn2+So956uWSce3h5rcQ8yrxWO2i1+taS UwYN/cgZdMyYPAhZw15kORvV7T+ggi4A92vDTXg+QpD7OOaVGgSEd65+Pkl9EzAZgB oBYtMuG/P2jezl1JIE0SCTDOlwP73Vrn7kvVdH8o= Authentication-Results: iva1-adaa4d2a0364.qloud-c.yandex.net; dkim=pass header.i=@syntacore.com From: Vladimir Isaev To: linux-arch@vger.kernel.org, linux-riscv@lists.infradead.org, Conor.Dooley@microchip.com, atishp@atishpatra.org, paul.walmsley@sifive.com, palmer@dabbelt.com, aou@eecs.berkeley.edu, anup@brainfault.org Cc: Vladimir Isaev Subject: [PATCH v2] riscv: Fix permissions for all mm's during mm init Date: Tue, 30 Aug 2022 12:16:12 +0300 Message-Id: <20220830091612.513137-1-vladimir.isaev@syntacore.com> X-Mailer: git-send-email 2.37.2 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220830_021731_097852_6DB234AE X-CRM114-Status: GOOD ( 15.94 ) X-BeenThere: linux-riscv@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-riscv" Errors-To: linux-riscv-bounces+linux-riscv=archiver.kernel.org@lists.infradead.org It is possible to have more than one mm (init_mm) during memory permission fixes. In my case it was caused by request_module from drivers/net/phy/phy_device.c and leads to following Oops during free_initmem() on RV32 platform: Unable to handle kernel paging request at virtual address c0800000 Oops [#1] Modules linked in: CPU: 0 PID: 1 Comm: swapper Not tainted 5.15.45 Hardware name: Syntacore SCR5 SDK board (DT) epc : __memset+0x58/0xf4 ra : free_reserved_area+0xfa/0x15a epc : c02b26ac ra : c00eb588 sp : c1c1fed0 gp : c1898690 tp : c1c98000 t0 : c0800000 t1 : ffffffff t2 : 00000000 s0 : c1c1ff20 s1 : c189a000 a0 : c0800000 a1 : cccccccc a2 : 00001000 a3 : c0801000 a4 : 00000000 a5 : 00800000 a6 : fef09000 a7 : 00000000 s2 : c0e57000 s3 : c10edcf8 s4 : 000000cc s5 : ffffefff s6 : c188a9f4 s7 : 00000001 s8 : c0800000 s9 : fef1b000 s10: c10ee000 s11: c189a000 t3 : 00000000 t4 : 00000000 t5 : 00000000 t6 : 00000001 status: 00000120 badaddr: c0800000 cause: 0000000f [] free_initmem+0x204/0x222 [] kernel_init+0x32/0xfc [] ret_from_exception+0x0/0xc ---[ end trace 7a5e2b002350b528 ]--- This is because request_module attempted to modprobe module, so it created new mm with the copy of kernel's page table. And this copy won't be updated in case of 4M pages and RV32 (pgd is the leaf). To fix this we can update protection bits for all of existing mm-s, the same as ARM does, see commit 08925c2f124f ("ARM: 8464/1: Update all mm structures with section adjustments"). Fixes: 19a00869028f ("RISC-V: Protect all kernel sections including init early") Signed-off-by: Vladimir Isaev Reviewed-by: Andrew Jones --- Changes for v2: - Fixed commit message format. - Added 'Fixes' tag. --- arch/riscv/include/asm/set_memory.h | 20 +++-------- arch/riscv/kernel/setup.c | 11 ------ arch/riscv/mm/init.c | 29 ++++++++++++--- arch/riscv/mm/pageattr.c | 55 ++++++++++++++++++++++++----- 4 files changed, 75 insertions(+), 40 deletions(-) diff --git a/arch/riscv/include/asm/set_memory.h b/arch/riscv/include/asm/set_memory.h index a2c14d4b3993..bb0f6b4ed86b 100644 --- a/arch/riscv/include/asm/set_memory.h +++ b/arch/riscv/include/asm/set_memory.h @@ -16,28 +16,16 @@ int set_memory_rw(unsigned long addr, int numpages); int set_memory_x(unsigned long addr, int numpages); int set_memory_nx(unsigned long addr, int numpages); int set_memory_rw_nx(unsigned long addr, int numpages); -static __always_inline int set_kernel_memory(char *startp, char *endp, - int (*set_memory)(unsigned long start, - int num_pages)) -{ - unsigned long start = (unsigned long)startp; - unsigned long end = (unsigned long)endp; - int num_pages = PAGE_ALIGN(end - start) >> PAGE_SHIFT; - - return set_memory(start, num_pages); -} +void fix_kernel_mem_early(char *startp, char *endp, pgprot_t set_mask, + pgprot_t clear_mask); #else static inline int set_memory_ro(unsigned long addr, int numpages) { return 0; } static inline int set_memory_rw(unsigned long addr, int numpages) { return 0; } static inline int set_memory_x(unsigned long addr, int numpages) { return 0; } static inline int set_memory_nx(unsigned long addr, int numpages) { return 0; } static inline int set_memory_rw_nx(unsigned long addr, int numpages) { return 0; } -static inline int set_kernel_memory(char *startp, char *endp, - int (*set_memory)(unsigned long start, - int num_pages)) -{ - return 0; -} +static inline void fix_kernel_mem_early(char *startp, char *endp, + pgprot_t set_mask, pgprot_t clear_mask) { } #endif int set_direct_map_invalid_noflush(struct page *page); diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index 95ef6e2bf45c..17eae1406092 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -318,13 +317,3 @@ static int __init topology_init(void) return 0; } subsys_initcall(topology_init); - -void free_initmem(void) -{ - if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) - set_kernel_memory(lm_alias(__init_begin), lm_alias(__init_end), - IS_ENABLED(CONFIG_64BIT) ? - set_memory_rw : set_memory_rw_nx); - - free_initmem_default(POISON_FREE_INITMEM); -} diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index b56a0a75533f..978202712535 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include "../kernel/head.h" @@ -714,10 +714,14 @@ static __init pgprot_t pgprot_from_va(uintptr_t va) void mark_rodata_ro(void) { - set_kernel_memory(__start_rodata, _data, set_memory_ro); - if (IS_ENABLED(CONFIG_64BIT)) - set_kernel_memory(lm_alias(__start_rodata), lm_alias(_data), - set_memory_ro); + pgprot_t set_mask = __pgprot(_PAGE_READ); + pgprot_t clear_mask = __pgprot(_PAGE_WRITE); + + fix_kernel_mem_early(__start_rodata, _data, set_mask, clear_mask); + if (IS_ENABLED(CONFIG_64BIT)) { + fix_kernel_mem_early(lm_alias(__start_rodata), lm_alias(_data), + set_mask, clear_mask); + } debug_checkwx(); } @@ -1243,3 +1247,18 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node, return vmemmap_populate_basepages(start, end, node, NULL); } #endif + +void free_initmem(void) +{ + pgprot_t set_mask = __pgprot(_PAGE_READ | _PAGE_WRITE); + pgprot_t clear_mask = IS_ENABLED(CONFIG_64BIT) ? + __pgprot(0) : __pgprot(_PAGE_EXEC); + + if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) { + fix_kernel_mem_early(lm_alias(__init_begin), + lm_alias(__init_end), + set_mask, clear_mask); + } + + free_initmem_default(POISON_FREE_INITMEM); +} diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c index 5e49e4b4a4cc..10a4ee313083 100644 --- a/arch/riscv/mm/pageattr.c +++ b/arch/riscv/mm/pageattr.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -104,24 +105,62 @@ static const struct mm_walk_ops pageattr_ops = { .pte_hole = pageattr_pte_hole, }; -static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, - pgprot_t clear_mask) +static int __set_memory_mm(struct mm_struct *mm, unsigned long start, + unsigned long end, pgprot_t set_mask, + pgprot_t clear_mask) { int ret; - unsigned long start = addr; - unsigned long end = start + PAGE_SIZE * numpages; struct pageattr_masks masks = { .set_mask = set_mask, .clear_mask = clear_mask }; + mmap_read_lock(mm); + ret = walk_page_range_novma(mm, start, end, &pageattr_ops, NULL, + &masks); + mmap_read_unlock(mm); + + return ret; +} + +void fix_kernel_mem_early(char *startp, char *endp, pgprot_t set_mask, + pgprot_t clear_mask) +{ + struct task_struct *t, *s; + + unsigned long start = (unsigned long)startp; + unsigned long end = PAGE_ALIGN((unsigned long)endp); + + __set_memory_mm(current->active_mm, start, end, set_mask, clear_mask); + __set_memory_mm(&init_mm, start, end, set_mask, clear_mask); + + rcu_read_lock(); + for_each_process(t) { + if (t->flags & PF_KTHREAD) + continue; + for_each_thread(t, s) { + if (s->mm) { + __set_memory_mm(s->mm, start, end, set_mask, + clear_mask); + } + } + } + rcu_read_unlock(); + + flush_tlb_kernel_range(start, end); +} + +static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, + pgprot_t clear_mask) +{ + int ret; + unsigned long start = addr; + unsigned long end = start + PAGE_SIZE * numpages; + if (!numpages) return 0; - mmap_read_lock(&init_mm); - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, - &masks); - mmap_read_unlock(&init_mm); + ret = __set_memory_mm(&init_mm, start, end, set_mask, clear_mask); flush_tlb_kernel_range(start, end);