All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc
@ 2017-06-28 17:04 Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 1/7] powerpc/lib/code-patching: Use alternate map for patch_instruction() Balbir Singh
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Balbir Singh @ 2017-06-28 17:04 UTC (permalink / raw)
  To: linuxppc-dev, mpe; +Cc: naveen.n.rao, christophe.leroy, paulus, Balbir Singh

Provide STRICT_KERNEL_RWX for PPC64/BOOK3S

These patches enable RX mappings of kernel text.
rodata is mapped RX as well as a trade-off, there
are more details in the patch description

As a prerequisite for R/O text, patch_instruction
is moved over to using a separate mapping that
allows write to kernel text. xmon/ftrace/kprobes
have been moved over to work with patch_instruction

There is a bug fix, the updatepp and updateboltedpp
(pseries) providers, did not use flags as described in
PAPR (patch 3). I would like to see that patch marked
to stable, I've not added a Cc:stable myself.

Another build failure was reported, because instead
of using ARCH_HAS_SET_MEMORY as a gate for set_memory.h
inclusion, some of the infrastructure in the core kernel
uses CONFIG_STRICT_KERNEL_RWX. I've sent a fix to the
fix the latter. It should be picked up by 4.13 at which
time we can remove the config dependency on !HIBERNATION
in arch/powerpc/Kconfig

This version received testing under CONFIG_RELOCATABLE_TEST.
CONFIG_STRICT_KERNEL_RWX does not work great with that config
and disables the feature when relocation is on and prints
a warning message. 

After these changes go in we can get the PPC32 varaint
for the same feature based on patches already posted
by Christophe.

Changelog v5:
	Drop patches accepted by Michael Ellerman
	The radix patch has been reworked to
	a. avoid splitting linear mapping when
	   CONFIG_STRICT_KERNEL_RWX is off
	b. Fix the psize used for the linear mapping
	There is also a bug fix to allow vector
	areas to remain executable.
Changelog v4:
	Multiple cleanups to patch_instruction() based on
	review comments from Michael Ellerman
	Changes to Kconfig to make the feature selectable
Changelog v3:
	Support radix
	Drop ptdump patch, already picked from v2
Changelog v2:
	Support optprobes via patch_instruction

Balbir Singh (7):
  powerpc/lib/code-patching: Use alternate map for patch_instruction()
  powerpc/vmlinux.lds: Align __init_begin to 16M
  powerpc/platform/pseries/lpar: Fix updatepp and updateboltedpp
  powerpc/mm/hash: Implement mark_rodata_ro() for hash
  powerpc/mm/radix: Implement mark_rodata_ro() for radix
  mm/radix: Fix permissions correctly for interrupt_vectors
  powerpc/Kconfig: Enable STRICT_KERNEL_RWX

 arch/powerpc/Kconfig                       |   2 +
 arch/powerpc/include/asm/book3s/64/hash.h  |   3 +
 arch/powerpc/include/asm/book3s/64/radix.h |   4 +
 arch/powerpc/kernel/vmlinux.lds.S          |  10 +-
 arch/powerpc/lib/code-patching.c           | 160 ++++++++++++++++++++++++++++-
 arch/powerpc/mm/pgtable-hash64.c           |  41 ++++++++
 arch/powerpc/mm/pgtable-radix.c            |  86 +++++++++++++++-
 arch/powerpc/mm/pgtable_64.c               |   9 ++
 arch/powerpc/platforms/pseries/lpar.c      |  21 +++-
 9 files changed, 327 insertions(+), 9 deletions(-)

-- 
2.9.4

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v5 1/7] powerpc/lib/code-patching: Use alternate map for patch_instruction()
  2017-06-28 17:04 [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc Balbir Singh
@ 2017-06-28 17:04 ` Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 2/7] powerpc/vmlinux.lds: Align __init_begin to 16M Balbir Singh
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Balbir Singh @ 2017-06-28 17:04 UTC (permalink / raw)
  To: linuxppc-dev, mpe; +Cc: naveen.n.rao, christophe.leroy, paulus, Balbir Singh

This patch creates the window using text_poke_area, allocated
via get_vm_area(). text_poke_area is per CPU to avoid locking.
text_poke_area for each cpu is setup using late_initcall, prior
to setup of these alternate mapping areas, we continue to use
direct write to change/modify kernel text. With the ability
to use alternate mappings to write to kernel text, it provides
us the freedom to then turn text read-only and implement
CONFIG_STRICT_KERNEL_RWX.

This code is CPU hotplug aware to ensure that the we have mappings
for any new cpus as they come online and tear down mappings for
any cpus that are offline.

Other arches do similar things, but use fixmaps. The reason
for not using fixmaps is to make use of any randomization in
the future.

Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
 arch/powerpc/lib/code-patching.c | 160 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 156 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c
index 500b0f6..19b8368 100644
--- a/arch/powerpc/lib/code-patching.c
+++ b/arch/powerpc/lib/code-patching.c
@@ -12,23 +12,172 @@
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 #include <linux/mm.h>
-#include <asm/page.h>
-#include <asm/code-patching.h>
+#include <linux/cpuhotplug.h>
+#include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <linux/kprobes.h>
 
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+#include <asm/page.h>
+#include <asm/code-patching.h>
 
-int patch_instruction(unsigned int *addr, unsigned int instr)
+static int __patch_instruction(unsigned int *addr, unsigned int instr)
 {
 	int err;
 
 	__put_user_size(instr, addr, 4, err);
 	if (err)
 		return err;
-	asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" : : "r" (addr));
+	asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" :: "r" (addr));
+	return 0;
+}
+
+#ifdef CONFIG_STRICT_KERNEL_RWX
+static DEFINE_PER_CPU(struct vm_struct *, text_poke_area);
+
+static int text_area_cpu_up(unsigned int cpu)
+{
+	struct vm_struct *area;
+
+	area = get_vm_area(PAGE_SIZE, VM_ALLOC);
+	if (!area) {
+		WARN_ONCE(1, "Failed to create text area for cpu %d\n",
+			cpu);
+		return -1;
+	}
+	this_cpu_write(text_poke_area, area);
+	return 0;
+}
+
+static int text_area_cpu_down(unsigned int cpu)
+{
+	free_vm_area(this_cpu_read(text_poke_area));
+	return 0;
+}
+
+/*
+ * This is an early_initcall and early_initcalls happen at the right time
+ * for us, after slab is enabled and before we mark ro pages R/O. In the
+ * future if get_vm_area is randomized, this will be more flexible than
+ * fixmap
+ */
+static int __init setup_text_poke_area(void)
+{
+	BUG_ON(!cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+		"powerpc/text_poke:online", text_area_cpu_up,
+		text_area_cpu_down));
+
+	pr_info("text_poke area ready...\n");
+	return 0;
+}
+
+/*
+ * This can be called for kernel text or a module.
+ */
+static int map_patch_area(void *addr, unsigned long text_poke_addr)
+{
+	unsigned long pfn;
+	int err;
+
+	if (is_vmalloc_addr(addr))
+		pfn = vmalloc_to_pfn(addr);
+	else
+		pfn = __pa_symbol(addr) >> PAGE_SHIFT;
+
+	err = map_kernel_page(text_poke_addr, (pfn << PAGE_SHIFT),
+				pgprot_val(PAGE_KERNEL));
+	pr_devel("Mapped addr %lx with pfn %lx:%d\n", text_poke_addr, pfn, err);
+	if (err)
+		return -1;
+	return 0;
+}
+
+static inline int unmap_patch_area(unsigned long addr)
+{
+	pte_t *ptep;
+	pmd_t *pmdp;
+	pud_t *pudp;
+	pgd_t *pgdp;
+
+	pgdp = pgd_offset_k(addr);
+	if (unlikely(!pgdp))
+		return -EINVAL;
+	pudp = pud_offset(pgdp, addr);
+	if (unlikely(!pudp))
+		return -EINVAL;
+	pmdp = pmd_offset(pudp, addr);
+	if (unlikely(!pmdp))
+		return -EINVAL;
+	ptep = pte_offset_kernel(pmdp, addr);
+	if (unlikely(!ptep))
+		return -EINVAL;
+
+	pr_devel("clearing mm %p, pte %p, addr %lx\n", &init_mm, ptep, addr);
+	/*
+	 * In hash, pte_clear flushes the tlb, in radix, we have to
+	 */
+	pte_clear(&init_mm, addr, ptep);
+	flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
 	return 0;
 }
 
+int patch_instruction(unsigned int *addr, unsigned int instr)
+{
+	int err;
+	unsigned int *dest = NULL;
+	unsigned long flags;
+	unsigned long text_poke_addr;
+	unsigned long kaddr = (unsigned long)addr;
+
+	/*
+	 * During early early boot patch_instruction is called
+	 * when text_poke_area is not ready, but we still need
+	 * to allow patching. We just do the plain old patching
+	 * We use slab_is_available and per cpu read * via this_cpu_read
+	 * of text_poke_area. Per-CPU areas might not be up early
+	 * this can create problems with just using this_cpu_read()
+	 */
+	if (!slab_is_available() || !this_cpu_read(text_poke_area))
+		return __patch_instruction(addr, instr);
+
+	local_irq_save(flags);
+
+	text_poke_addr = (unsigned long)__this_cpu_read(text_poke_area)->addr;
+	if (map_patch_area(addr, text_poke_addr)) {
+		err = -1;
+		goto out;
+	}
+
+	dest = (unsigned int *)(text_poke_addr) +
+			((kaddr & ~PAGE_MASK) / sizeof(unsigned int));
+
+	/*
+	 * We use __put_user_size so that we can handle faults while
+	 * writing to dest and return err to handle faults gracefully
+	 */
+	__put_user_size(instr, dest, 4, err);
+	if (!err)
+		asm ("dcbst 0, %0; sync; icbi 0,%0; icbi 0,%1; sync; isync"
+			::"r" (dest), "r"(addr));
+	err = unmap_patch_area(text_poke_addr);
+	if (err)
+		pr_warn("failed to unmap %lx\n", text_poke_addr);
+out:
+	local_irq_restore(flags);
+	return err;
+}
+
+#else /* !CONFIG_STRICT_KERNEL_RWX */
+
+int patch_instruction(unsigned int *addr, unsigned int instr)
+{
+	return __patch_instruction(addr, instr);
+}
+
+#endif /* CONFIG_STRICT_KERNEL_RWX */
+NOKPROBE_SYMBOL(patch_instruction);
+
 int patch_branch(unsigned int *addr, unsigned long target, int flags)
 {
 	return patch_instruction(addr, create_branch(addr, target, flags));
@@ -514,3 +663,6 @@ static int __init test_code_patching(void)
 late_initcall(test_code_patching);
 
 #endif /* CONFIG_CODE_PATCHING_SELFTEST */
+#ifdef CONFIG_STRICT_KERNEL_RWX
+late_initcall(setup_text_poke_area);
+#endif
-- 
2.9.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 2/7] powerpc/vmlinux.lds: Align __init_begin to 16M
  2017-06-28 17:04 [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 1/7] powerpc/lib/code-patching: Use alternate map for patch_instruction() Balbir Singh
@ 2017-06-28 17:04 ` Balbir Singh
  2017-06-29 11:29   ` David Laight
  2017-06-28 17:04 ` [PATCH v5 3/7] powerpc/platform/pseries/lpar: Fix updatepp and updateboltedpp Balbir Singh
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: Balbir Singh @ 2017-06-28 17:04 UTC (permalink / raw)
  To: linuxppc-dev, mpe; +Cc: naveen.n.rao, christophe.leroy, paulus, Balbir Singh

For CONFIG_STRICT_KERNEL_RWX align __init_begin to 16M.
We use 16M since its the larger of 2M on radix and 16M
on hash for our linear mapping. The plan is to have
.text, .rodata and everything upto __init_begin marked
as RX. Note we still have executable read only data.
We could further align rodata to another 16M boundary.
I've used keeping text plus rodata as read-only-executable
as a trade-off to doing read-only-executable for text and
read-only for rodata.

We don't use multi PT_LOAD in PHDRS because we are
not sure if all bootloaders support them. This patch keeps
PHDRS in vmlinux.lds.S as the same they are with just one
PT_LOAD for all of the kernel marked as RWX (7).

Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
 arch/powerpc/kernel/vmlinux.lds.S | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index ace6b65..b1a2505 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -8,6 +8,12 @@
 #include <asm/cache.h>
 #include <asm/thread_info.h>
 
+#ifdef CONFIG_STRICT_KERNEL_RWX
+#define STRICT_ALIGN_SIZE	(1 << 24)
+#else
+#define STRICT_ALIGN_SIZE	PAGE_SIZE
+#endif
+
 ENTRY(_stext)
 
 PHDRS {
@@ -123,7 +129,7 @@ SECTIONS
 	PROVIDE32 (etext = .);
 
 	/* Read-only data */
-	RODATA
+	RO_DATA(PAGE_SIZE)
 
 	EXCEPTION_TABLE(0)
 
@@ -140,7 +146,7 @@ SECTIONS
 /*
  * Init sections discarded at runtime
  */
-	. = ALIGN(PAGE_SIZE);
+	. = ALIGN(STRICT_ALIGN_SIZE);
 	__init_begin = .;
 	INIT_TEXT_SECTION(PAGE_SIZE) :kernel
 
-- 
2.9.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 3/7] powerpc/platform/pseries/lpar: Fix updatepp and updateboltedpp
  2017-06-28 17:04 [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 1/7] powerpc/lib/code-patching: Use alternate map for patch_instruction() Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 2/7] powerpc/vmlinux.lds: Align __init_begin to 16M Balbir Singh
@ 2017-06-28 17:04 ` Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 4/7] powerpc/mm/hash: Implement mark_rodata_ro() for hash Balbir Singh
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Balbir Singh @ 2017-06-28 17:04 UTC (permalink / raw)
  To: linuxppc-dev, mpe; +Cc: naveen.n.rao, christophe.leroy, paulus, Balbir Singh

PAPR has pp0 in bit 55, currently we assumed that bit
pp0 is bit 0 (all bits in IBM order). This patch fixes
the pp0 bits for both these routines that use H_PROTECT.

Fixes (e58e87a powerpc/mm: Update _PAGE_KERNEL_RO)

Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
 arch/powerpc/platforms/pseries/lpar.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 6541d0b..2d36571 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -301,7 +301,7 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot,
 				       int ssize, unsigned long inv_flags)
 {
 	unsigned long lpar_rc;
-	unsigned long flags = (newpp & 7) | H_AVPN;
+	unsigned long flags;
 	unsigned long want_v;
 
 	want_v = hpte_encode_avpn(vpn, psize, ssize);
@@ -309,6 +309,15 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot,
 	pr_devel("    update: avpnv=%016lx, hash=%016lx, f=%lx, psize: %d ...",
 		 want_v, slot, flags, psize);
 
+	/*
+	 * Move pp0 and set the mask, pp0 is bit 55
+	 * We ignore the keys for now.
+	 */
+	if (mmu_has_feature(MMU_FTR_KERNEL_RO))
+		flags = ((newpp & HPTE_R_PP0) >> 55) | (newpp & 7) | H_AVPN;
+	else
+		flags = (newpp & 7) | H_AVPN;
+
 	lpar_rc = plpar_pte_protect(flags, slot, want_v);
 
 	if (lpar_rc == H_NOT_FOUND) {
@@ -379,7 +388,15 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp,
 	slot = pSeries_lpar_hpte_find(vpn, psize, ssize);
 	BUG_ON(slot == -1);
 
-	flags = newpp & 7;
+	/*
+	 * Move pp0 and set the mask, pp0 is bit 55
+	 * We ignore the keys for now.
+	 */
+	if (mmu_has_feature(MMU_FTR_KERNEL_RO))
+		flags = ((newpp & HPTE_R_PP0) >> 55) | (newpp & 7);
+	else
+		flags = (newpp & 7);
+
 	lpar_rc = plpar_pte_protect(flags, slot, 0);
 
 	BUG_ON(lpar_rc != H_SUCCESS);
-- 
2.9.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 4/7] powerpc/mm/hash: Implement mark_rodata_ro() for hash
  2017-06-28 17:04 [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc Balbir Singh
                   ` (2 preceding siblings ...)
  2017-06-28 17:04 ` [PATCH v5 3/7] powerpc/platform/pseries/lpar: Fix updatepp and updateboltedpp Balbir Singh
@ 2017-06-28 17:04 ` Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 5/7] powerpc/mm/radix: Implement mark_rodata_ro() for radix Balbir Singh
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Balbir Singh @ 2017-06-28 17:04 UTC (permalink / raw)
  To: linuxppc-dev, mpe; +Cc: naveen.n.rao, christophe.leroy, paulus, Balbir Singh

With hash we update the bolted pte to mark it read-only. We rely
on the MMU_FTR_KERNEL_RO to generate the correct permissions
for read-only text. The radix implementation just prints a warning
in this implementation

Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
 arch/powerpc/include/asm/book3s/64/hash.h  |  3 +++
 arch/powerpc/include/asm/book3s/64/radix.h |  4 +++
 arch/powerpc/mm/pgtable-hash64.c           | 41 ++++++++++++++++++++++++++++++
 arch/powerpc/mm/pgtable-radix.c            |  7 +++++
 arch/powerpc/mm/pgtable_64.c               |  9 +++++++
 5 files changed, 64 insertions(+)

diff --git a/arch/powerpc/include/asm/book3s/64/hash.h b/arch/powerpc/include/asm/book3s/64/hash.h
index 4e957b0..0ce513f 100644
--- a/arch/powerpc/include/asm/book3s/64/hash.h
+++ b/arch/powerpc/include/asm/book3s/64/hash.h
@@ -89,6 +89,9 @@ static inline int hash__pgd_bad(pgd_t pgd)
 {
 	return (pgd_val(pgd) == 0);
 }
+#ifdef CONFIG_STRICT_KERNEL_RWX
+extern void hash__mark_rodata_ro(void);
+#endif
 
 extern void hpte_need_flush(struct mm_struct *mm, unsigned long addr,
 			    pte_t *ptep, unsigned long pte, int huge);
diff --git a/arch/powerpc/include/asm/book3s/64/radix.h b/arch/powerpc/include/asm/book3s/64/radix.h
index ac16d19..368cb54 100644
--- a/arch/powerpc/include/asm/book3s/64/radix.h
+++ b/arch/powerpc/include/asm/book3s/64/radix.h
@@ -116,6 +116,10 @@
 #define RADIX_PUD_TABLE_SIZE	(sizeof(pud_t) << RADIX_PUD_INDEX_SIZE)
 #define RADIX_PGD_TABLE_SIZE	(sizeof(pgd_t) << RADIX_PGD_INDEX_SIZE)
 
+#ifdef CONFIG_STRICT_KERNEL_RWX
+extern void radix__mark_rodata_ro(void);
+#endif
+
 static inline unsigned long __radix_pte_update(pte_t *ptep, unsigned long clr,
 					       unsigned long set)
 {
diff --git a/arch/powerpc/mm/pgtable-hash64.c b/arch/powerpc/mm/pgtable-hash64.c
index 8b85a14..7e9c924 100644
--- a/arch/powerpc/mm/pgtable-hash64.c
+++ b/arch/powerpc/mm/pgtable-hash64.c
@@ -11,8 +11,12 @@
 
 #include <linux/sched.h>
 #include <linux/mm_types.h>
+#include <linux/mm.h>
 
 #include <asm/pgalloc.h>
+#include <asm/pgtable.h>
+#include <asm/sections.h>
+#include <asm/mmu.h>
 #include <asm/tlb.h>
 
 #include "mmu_decl.h"
@@ -342,3 +346,40 @@ int hash__has_transparent_hugepage(void)
 	return 1;
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
+
+#ifdef CONFIG_STRICT_KERNEL_RWX
+void hash__mark_rodata_ro(void)
+{
+	unsigned long start = (unsigned long)_stext;
+	unsigned long end = (unsigned long)__init_begin;
+	unsigned long idx;
+	unsigned int step, shift;
+	unsigned long newpp = PP_RXXX;
+
+	if (!mmu_has_feature(MMU_FTR_KERNEL_RO)) {
+		pr_info("R/O rodata not supported\n");
+		return;
+	}
+
+	shift = mmu_psize_defs[mmu_linear_psize].shift;
+	step = 1 << shift;
+
+	start = ((start + step - 1) >> shift) << shift;
+	end = (end >> shift) << shift;
+
+	pr_devel("marking ro start %lx, end %lx, step %x\n",
+			start, end, step);
+
+	if (start == end) {
+		pr_warn("could not set rodata ro, relocate the start"
+			" of the kernel to a 0x%x boundary\n", step);
+		return;
+	}
+
+	for (idx = start; idx < end; idx += step)
+		/* Not sure if we can do much with the return value */
+		mmu_hash_ops.hpte_updateboltedpp(newpp, idx, mmu_linear_psize,
+							mmu_kernel_ssize);
+
+}
+#endif
diff --git a/arch/powerpc/mm/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c
index 6c062f9..0797c4e 100644
--- a/arch/powerpc/mm/pgtable-radix.c
+++ b/arch/powerpc/mm/pgtable-radix.c
@@ -109,6 +109,13 @@ int radix__map_kernel_page(unsigned long ea, unsigned long pa,
 	return 0;
 }
 
+#ifdef CONFIG_STRICT_KERNEL_RWX
+void radix__mark_rodata_ro(void)
+{
+	pr_warn("Not yet implemented for radix\n");
+}
+#endif
+
 static inline void __meminit print_mapping(unsigned long start,
 					   unsigned long end,
 					   unsigned long size)
diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
index 8d2d674..34d8633 100644
--- a/arch/powerpc/mm/pgtable_64.c
+++ b/arch/powerpc/mm/pgtable_64.c
@@ -487,3 +487,12 @@ void mmu_partition_table_set_entry(unsigned int lpid, unsigned long dw0,
 }
 EXPORT_SYMBOL_GPL(mmu_partition_table_set_entry);
 #endif /* CONFIG_PPC_BOOK3S_64 */
+
+#ifdef CONFIG_STRICT_KERNEL_RWX
+void mark_rodata_ro(void)
+{
+	if (radix_enabled())
+		return radix__mark_rodata_ro();
+	return hash__mark_rodata_ro();
+}
+#endif
-- 
2.9.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 5/7] powerpc/mm/radix: Implement mark_rodata_ro() for radix
  2017-06-28 17:04 [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc Balbir Singh
                   ` (3 preceding siblings ...)
  2017-06-28 17:04 ` [PATCH v5 4/7] powerpc/mm/hash: Implement mark_rodata_ro() for hash Balbir Singh
@ 2017-06-28 17:04 ` Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 6/7] mm/radix: Fix permissions correctly for interrupt_vectors Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 7/7] powerpc/Kconfig: Enable STRICT_KERNEL_RWX Balbir Singh
  6 siblings, 0 replies; 10+ messages in thread
From: Balbir Singh @ 2017-06-28 17:04 UTC (permalink / raw)
  To: linuxppc-dev, mpe; +Cc: naveen.n.rao, christophe.leroy, paulus, Balbir Singh

The patch splits the linear page mapping such that
the ones with kernel text are mapped as 2M and others
are mapped with the largest possible size - 1G. The downside
of this is that we split a 1G mapping into 512 2M mappings
for the kernel, but in the absence of that we cannot support
R/O areas in 1G, the kernel size is much smaller and using
1G as the granularity will waste a lot of space at the cost
of optimizing the TLB. The text itself should fit into about
6-8 mappings, so the effect should not be all that bad.

Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
 arch/powerpc/mm/pgtable-radix.c | 73 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 71 insertions(+), 2 deletions(-)

diff --git a/arch/powerpc/mm/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c
index 0797c4e..6dc9923 100644
--- a/arch/powerpc/mm/pgtable-radix.c
+++ b/arch/powerpc/mm/pgtable-radix.c
@@ -11,6 +11,7 @@
 #include <linux/sched/mm.h>
 #include <linux/memblock.h>
 #include <linux/of_fdt.h>
+#include <linux/mm.h>
 
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -23,6 +24,8 @@
 
 #include <trace/events/thp.h>
 
+int mmu_radix_linear_psize = PAGE_SIZE;
+
 static int native_register_process_table(unsigned long base, unsigned long pg_sz,
 					 unsigned long table_size)
 {
@@ -112,7 +115,53 @@ int radix__map_kernel_page(unsigned long ea, unsigned long pa,
 #ifdef CONFIG_STRICT_KERNEL_RWX
 void radix__mark_rodata_ro(void)
 {
-	pr_warn("Not yet implemented for radix\n");
+	unsigned long start = (unsigned long)_stext;
+	unsigned long end = (unsigned long)__init_begin;
+	unsigned long idx;
+	unsigned int step, shift;
+	pgd_t *pgdp;
+	pud_t *pudp;
+	pmd_t *pmdp;
+	pte_t *ptep;
+
+	if (!mmu_has_feature(MMU_FTR_KERNEL_RO)) {
+		pr_info("R/O rodata not supported\n");
+		return;
+	}
+
+	shift = ilog2(mmu_radix_linear_psize);
+	step = 1 << shift;
+
+	start = ((start + step - 1) >> shift) << shift;
+	end = (end >> shift) << shift;
+
+	pr_devel("marking ro start %lx, end %lx, step %x\n",
+			start, end, step);
+
+	for (idx = start; idx < end; idx += step) {
+		pgdp = pgd_offset_k(idx);
+		pudp = pud_alloc(&init_mm, pgdp, idx);
+		if (!pudp)
+			continue;
+		if (pud_huge(*pudp)) {
+			ptep = (pte_t *)pudp;
+			goto update_the_pte;
+		}
+		pmdp = pmd_alloc(&init_mm, pudp, idx);
+		if (!pmdp)
+			continue;
+		if (pmd_huge(*pmdp)) {
+			ptep = pmdp_ptep(pmdp);
+			goto update_the_pte;
+		}
+		ptep = pte_alloc_kernel(pmdp, idx);
+		if (!ptep)
+			continue;
+update_the_pte:
+		radix__pte_update(&init_mm, idx, ptep, _PAGE_WRITE, 0, 0);
+	}
+	radix__flush_tlb_kernel_range(start, end);
+
 }
 #endif
 
@@ -131,6 +180,12 @@ static int __meminit create_physical_mapping(unsigned long start,
 {
 	unsigned long vaddr, addr, mapping_size = 0;
 	pgprot_t prot;
+	unsigned long max_mapping_size;
+#ifdef CONFIG_STRICT_KERNEL_RWX
+	int split_text_mapping = 1;
+#else
+	int split_text_mapping = 0;
+#endif
 
 	start = _ALIGN_UP(start, PAGE_SIZE);
 	for (addr = start; addr < end; addr += mapping_size) {
@@ -139,9 +194,12 @@ static int __meminit create_physical_mapping(unsigned long start,
 
 		gap = end - addr;
 		previous_size = mapping_size;
+		max_mapping_size = PUD_SIZE;
 
+retry:
 		if (IS_ALIGNED(addr, PUD_SIZE) && gap >= PUD_SIZE &&
-		    mmu_psize_defs[MMU_PAGE_1G].shift)
+		    mmu_psize_defs[MMU_PAGE_1G].shift &&
+		    PUD_SIZE <= max_mapping_size)
 			mapping_size = PUD_SIZE;
 		else if (IS_ALIGNED(addr, PMD_SIZE) && gap >= PMD_SIZE &&
 			 mmu_psize_defs[MMU_PAGE_2M].shift)
@@ -149,6 +207,17 @@ static int __meminit create_physical_mapping(unsigned long start,
 		else
 			mapping_size = PAGE_SIZE;
 
+		if (split_text_mapping && (mapping_size == PUD_SIZE) &&
+			(addr <= __pa_symbol(__init_begin)) &&
+			(addr + mapping_size) >= __pa_symbol(_stext)) {
+			max_mapping_size = PMD_SIZE;
+			goto retry;
+		}
+
+		if (addr <= __pa_symbol(__init_begin) &&
+			(addr + mapping_size) >= __pa_symbol(_stext))
+			mmu_radix_linear_psize = mapping_size;
+
 		if (mapping_size != previous_size) {
 			print_mapping(start, addr, previous_size);
 			start = addr;
-- 
2.9.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 6/7] mm/radix: Fix permissions correctly for interrupt_vectors
  2017-06-28 17:04 [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc Balbir Singh
                   ` (4 preceding siblings ...)
  2017-06-28 17:04 ` [PATCH v5 5/7] powerpc/mm/radix: Implement mark_rodata_ro() for radix Balbir Singh
@ 2017-06-28 17:04 ` Balbir Singh
  2017-06-28 17:04 ` [PATCH v5 7/7] powerpc/Kconfig: Enable STRICT_KERNEL_RWX Balbir Singh
  6 siblings, 0 replies; 10+ messages in thread
From: Balbir Singh @ 2017-06-28 17:04 UTC (permalink / raw)
  To: linuxppc-dev, mpe; +Cc: naveen.n.rao, christophe.leroy, paulus, Balbir Singh

Commit 9abcc98 introduced marking kernel text as executable,
but missed some bits of the changes from hash_utils_64.c, where
under CONFIG_RELOCATABLE with CONFIG_RELOCATABLE_TEST or a kdump
kernel, if the size of the mapping was less than 1G, we end up
marking the first page (address range 0->mapping_size) as non
executable. As a side-effect none of the exception handlers
work

fixes - Commit 9abcc981de97
("powerpc/mm/radix: Only add X for pages overlapping kernel text")

Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
 arch/powerpc/mm/pgtable-radix.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/powerpc/mm/pgtable-radix.c b/arch/powerpc/mm/pgtable-radix.c
index 6dc9923..ec21f97 100644
--- a/arch/powerpc/mm/pgtable-radix.c
+++ b/arch/powerpc/mm/pgtable-radix.c
@@ -230,6 +230,14 @@ static int __meminit create_physical_mapping(unsigned long start,
 		else
 			prot = PAGE_KERNEL;
 
+		/*
+		 * See the comment in hash_utils_64.c
+		 */
+		if ((PHYSICAL_START > MEMORY_START) &&
+			overlaps_interrupt_vector_text(vaddr,
+							vaddr + mapping_size))
+			prot = PAGE_KERNEL_X;
+
 		rc = radix__map_kernel_page(vaddr, addr, prot, mapping_size);
 		if (rc)
 			return rc;
-- 
2.9.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v5 7/7] powerpc/Kconfig: Enable STRICT_KERNEL_RWX
  2017-06-28 17:04 [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc Balbir Singh
                   ` (5 preceding siblings ...)
  2017-06-28 17:04 ` [PATCH v5 6/7] mm/radix: Fix permissions correctly for interrupt_vectors Balbir Singh
@ 2017-06-28 17:04 ` Balbir Singh
  6 siblings, 0 replies; 10+ messages in thread
From: Balbir Singh @ 2017-06-28 17:04 UTC (permalink / raw)
  To: linuxppc-dev, mpe; +Cc: naveen.n.rao, christophe.leroy, paulus, Balbir Singh

We have the basic support in the form of patching R/O
text sections, linker scripts for extending alignment
of text data. We've also got mark_rodata_ro()

NOTE: There is a temporary work-around for disabling
STRICT_KERNEL_RWX if CONFIG_HIBERNATION is enabled

Signed-off-by: Balbir Singh <bsingharora@gmail.com>
---
 arch/powerpc/Kconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 7d95c1d..cda69f3 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -163,6 +163,8 @@ config PPC
 	select HAVE_ARCH_MMAP_RND_COMPAT_BITS	if COMPAT
 	select HAVE_ARCH_SECCOMP_FILTER
 	select HAVE_ARCH_TRACEHOOK
+	select ARCH_HAS_STRICT_KERNEL_RWX	if (PPC_BOOK3S_64 && !HIBERNATION)
+	select ARCH_OPTIONAL_KERNEL_RWX		if ARCH_HAS_STRICT_KERNEL_RWX
 	select HAVE_CBPF_JIT			if !PPC64
 	select HAVE_CONTEXT_TRACKING		if PPC64
 	select HAVE_DEBUG_KMEMLEAK
-- 
2.9.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* RE: [PATCH v5 2/7] powerpc/vmlinux.lds: Align __init_begin to 16M
  2017-06-28 17:04 ` [PATCH v5 2/7] powerpc/vmlinux.lds: Align __init_begin to 16M Balbir Singh
@ 2017-06-29 11:29   ` David Laight
  2017-06-29 11:36     ` Balbir Singh
  0 siblings, 1 reply; 10+ messages in thread
From: David Laight @ 2017-06-29 11:29 UTC (permalink / raw)
  To: 'Balbir Singh', linuxppc-dev, mpe; +Cc: naveen.n.rao, paulus

From: Balbir Singh
> Sent: 28 June 2017 18:04
> For CONFIG_STRICT_KERNEL_RWX align __init_begin to 16M.
> We use 16M since its the larger of 2M on radix and 16M
> on hash for our linear mapping. The plan is to have
> .text, .rodata and everything upto __init_begin marked
> as RX. Note we still have executable read only data.
> We could further align rodata to another 16M boundary.
> I've used keeping text plus rodata as read-only-executable
> as a trade-off to doing read-only-executable for text and
> read-only for rodata.
...

Doesn't this go against 'address space randomisation'?
(Yes I realise a PIC kernel is probably non-trivial to compile
and load.)

	David

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v5 2/7] powerpc/vmlinux.lds: Align __init_begin to 16M
  2017-06-29 11:29   ` David Laight
@ 2017-06-29 11:36     ` Balbir Singh
  0 siblings, 0 replies; 10+ messages in thread
From: Balbir Singh @ 2017-06-29 11:36 UTC (permalink / raw)
  To: David Laight; +Cc: linuxppc-dev, mpe, naveen.n.rao, paulus

On Thu, Jun 29, 2017 at 9:29 PM, David Laight <David.Laight@aculab.com> wrote:
> From: Balbir Singh
>> Sent: 28 June 2017 18:04
>> For CONFIG_STRICT_KERNEL_RWX align __init_begin to 16M.
>> We use 16M since its the larger of 2M on radix and 16M
>> on hash for our linear mapping. The plan is to have
>> .text, .rodata and everything upto __init_begin marked
>> as RX. Note we still have executable read only data.
>> We could further align rodata to another 16M boundary.
>> I've used keeping text plus rodata as read-only-executable
>> as a trade-off to doing read-only-executable for text and
>> read-only for rodata.
> ...
>
> Doesn't this go against 'address space randomisation'?
> (Yes I realise a PIC kernel is probably non-trivial to compile
> and load.)

I presume you mean Kernel ASLR. I am not sure why you think
it goes against KASLR, it's just aligning the _stext and __init_begin,
they can be anywhere in memory. I've tested with a relocatable kernel

Balbir Singh.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2017-06-29 11:36 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-28 17:04 [PATCH v5 0/7] Provide STRICT_KERNEL_RWX for powerpc Balbir Singh
2017-06-28 17:04 ` [PATCH v5 1/7] powerpc/lib/code-patching: Use alternate map for patch_instruction() Balbir Singh
2017-06-28 17:04 ` [PATCH v5 2/7] powerpc/vmlinux.lds: Align __init_begin to 16M Balbir Singh
2017-06-29 11:29   ` David Laight
2017-06-29 11:36     ` Balbir Singh
2017-06-28 17:04 ` [PATCH v5 3/7] powerpc/platform/pseries/lpar: Fix updatepp and updateboltedpp Balbir Singh
2017-06-28 17:04 ` [PATCH v5 4/7] powerpc/mm/hash: Implement mark_rodata_ro() for hash Balbir Singh
2017-06-28 17:04 ` [PATCH v5 5/7] powerpc/mm/radix: Implement mark_rodata_ro() for radix Balbir Singh
2017-06-28 17:04 ` [PATCH v5 6/7] mm/radix: Fix permissions correctly for interrupt_vectors Balbir Singh
2017-06-28 17:04 ` [PATCH v5 7/7] powerpc/Kconfig: Enable STRICT_KERNEL_RWX Balbir Singh

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.