From: Jordan Niethe <hidden> Date: 2021-03-16 03:18:59
From: Russell Currey <redacted>
The set_memory_{ro/rw/nx/x}() functions are required for STRICT_MODULE_RWX,
and are generally useful primitives to have. This implementation is
designed to be completely generic across powerpc's many MMUs.
It's possible that this could be optimised to be faster for specific
MMUs, but the focus is on having a generic and safe implementation for
now.
This implementation does not handle cases where the caller is attempting
to change the mapping of the page it is executing from, or if another
CPU is concurrently using the page being altered. These cases likely
shouldn't happen, but a more complex implementation with MMU-specific code
could safely handle them, so that is left as a TODO for now.
These functions do nothing if STRICT_KERNEL_RWX is not enabled.
Reviewed-by: Daniel Axtens <redacted>
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Christophe Leroy <redacted>
[jpn: rebase on next plus "powerpc/mm/64s: Allow STRICT_KERNEL_RWX again"]
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/set_memory.h | 32 +++++++++++
arch/powerpc/mm/Makefile | 2 +-
arch/powerpc/mm/pageattr.c | 81 +++++++++++++++++++++++++++
4 files changed, 115 insertions(+), 1 deletion(-)
create mode 100644 arch/powerpc/include/asm/set_memory.h
create mode 100644 arch/powerpc/mm/pageattr.c
@@ -0,0 +1,81 @@+// SPDX-License-Identifier: GPL-2.0++/*+*MMU-genericset_memoryimplementationforpowerpc+*+*Copyright2019,IBMCorporation.+*/++#include<linux/mm.h>+#include<linux/set_memory.h>++#include<asm/mmu.h>+#include<asm/page.h>+#include<asm/pgtable.h>+++/*+*Updatestheattributesofapageinthreesteps:+*+*1.invalidatethepagetableentry+*2.flushtheTLB+*3.installthenewentrywiththeupdatedattributes+*+*Thisisunsafeifthecallerisattemptingtochangethemappingofthe+*pageitisexecutingfrom,orifanotherCPUisconcurrentlyusingthe+*pagebeingaltered.+*+*TODOmaketheimplementationresistanttothis.+*+*NOTE:canbedangeroustocallwithoutSTRICT_KERNEL_RWX+*/+staticintchange_page_attr(pte_t*ptep,unsignedlongaddr,void*data)+{+longaction=(long)data;+pte_tpte;++spin_lock(&init_mm.page_table_lock);++/* invalidate the PTE so it's safe to modify */+pte=ptep_get_and_clear(&init_mm,addr,ptep);+flush_tlb_kernel_range(addr,addr+PAGE_SIZE);++/* modify the PTE bits as desired, then apply */+switch(action){+caseSET_MEMORY_RO:+pte=pte_wrprotect(pte);+break;+caseSET_MEMORY_RW:+pte=pte_mkwrite(pte);+break;+caseSET_MEMORY_NX:+pte=pte_exprotect(pte);+break;+caseSET_MEMORY_X:+pte=pte_mkexec(pte);+break;+default:+WARN_ON_ONCE(1);+break;+}++set_pte_at(&init_mm,addr,ptep,pte);+spin_unlock(&init_mm.page_table_lock);++return0;+}++intchange_memory_attr(unsignedlongaddr,intnumpages,longaction)+{+unsignedlongstart=ALIGN_DOWN(addr,PAGE_SIZE);+unsignedlongsz=numpages*PAGE_SIZE;++if(!IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))+return0;++if(numpages<=0)+return0;++returnapply_to_existing_page_range(&init_mm,start,sz,+change_page_attr,(void*)action);+}
From: Jordan Niethe <hidden> Date: 2021-03-16 03:19:23
setup_text_poke_area() is a late init call so it runs before
mark_rodata_ro() and after the init calls. This lets all the init code
patching simply write to their locations. In the future, kprobes is
going to allocate its instruction pages RO which means they will need
setup_text__poke_area() to have been already called for their code
patching. However, init_kprobes() (which allocates and patches some
instruction pages) is an early init call so it happens before
setup_text__poke_area().
start_kernel() calls poking_init() before any of the init calls. On
powerpc, poking_init() is currently a nop. setup_text_poke_area() relies
on kernel virtual memory, cpu hotplug and per_cpu_areas being setup.
setup_per_cpu_areas(), boot_cpu_hotplug_init() and mm_init() are called
before poking_init().
Turn setup_text_poke_area() into poking_init().
Signed-off-by: Jordan Niethe <redacted>
---
v9: New to series
---
arch/powerpc/lib/code-patching.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
@@ -65,14 +65,11 @@ static int text_area_cpu_down(unsigned int cpu)}/*-*Runasalateinitcall.Thisallowsalltheboottimepatchingtobedone-*simplybypatchingthecode,andthenwe'recalledherepriorto-*mark_rodata_ro(),whichhappensafterallinitcallsarerun.Although-*BUG_ON()isrude,inthiscaseitshouldonlyhappenifENOMEM,andwejudge-*itasbeingpreferabletoakernelthatwillcrashlaterwhensomeonetries-*tousepatch_instruction().+*AlthoughBUG_ON()isrude,inthiscaseitshouldonlyhappenifENOMEM,and+*wejudgeitasbeingpreferabletoakernelthatwillcrashlaterwhen+*someonetriestousepatch_instruction().*/-staticint__initsetup_text_poke_area(void)+int__initpoking_init(void){BUG_ON(!cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,"powerpc/text_poke:online",text_area_cpu_up,
@@ -80,7 +77,6 @@ static int __init setup_text_poke_area(void)return0;}-late_initcall(setup_text_poke_area);/**Thiscanbecalledforkerneltextoramodule.
From: Jordan Niethe <hidden> Date: 2021-03-16 03:19:47
From: Russell Currey <redacted>
With CONFIG_STRICT_KERNEL_RWX=y and CONFIG_KPROBES=y, there will be one
W+X page at boot by default. This can be tested with
CONFIG_PPC_PTDUMP=y and CONFIG_PPC_DEBUG_WX=y set, and checking the
kernel log during boot.
Add an arch specific insn page allocator which returns RO pages if
STRICT_KERNEL_RWX is enabled. This page is only written to with
patch_instruction() which is able to write RO pages.
Reviewed-by: Daniel Axtens <redacted>
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Christophe Leroy <redacted>
[jpn: Reword commit message, switch from vmalloc_exec(), add
free_insn_page()]
Signed-off-by: Jordan Niethe <redacted>
---
v9: - vmalloc_exec() no longer exists
- Set the page to RW before freeing it
---
arch/powerpc/kernel/kprobes.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
@@ -371,7 +371,7 @@ config PPC_PTDUMPIfyouareunsure,sayN.configPPC_DEBUG_WX-bool"Warn on W+X mappings at boot"+bool"Warn on W+X mappings at boot & enable manual checks at runtime"depends onPPC_PTDUMP&&STRICT_KERNEL_RWXhelpGenerateawarningifanyW+Xmappingsarefoundatboot.
From: Jordan Niethe <hidden> Date: 2021-03-16 03:20:42
From: Russell Currey <redacted>
To enable strict module RWX on powerpc, set:
CONFIG_STRICT_MODULE_RWX=y
You should also have CONFIG_STRICT_KERNEL_RWX=y set to have any real
security benefit.
ARCH_HAS_STRICT_MODULE_RWX is set to require ARCH_HAS_STRICT_KERNEL_RWX.
This is due to a quirk in arch/Kconfig and arch/powerpc/Kconfig that
makes STRICT_MODULE_RWX *on by default* in configurations where
STRICT_KERNEL_RWX is *unavailable*.
Since this doesn't make much sense, and module RWX without kernel RWX
doesn't make much sense, having the same dependencies as kernel RWX
works around this problem.
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/Kconfig | 1 +
1 file changed, 1 insertion(+)
From: Jordan Niethe <hidden> Date: 2021-03-16 03:21:14
From: Russell Currey <redacted>
skiroot_defconfig is the only powerpc defconfig with STRICT_KERNEL_RWX
enabled, and if you want memory protection for kernel text you'd want it
for modules too, so enable STRICT_MODULE_RWX there.
Acked-by: Joel Stanley <redacted>
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/configs/skiroot_defconfig | 1 +
1 file changed, 1 insertion(+)
From: Jordan Niethe <hidden> Date: 2021-03-16 03:21:37
From: Christophe Leroy <redacted>
In addition to the set_memory_xx() functions which allows to change
the memory attributes of not (yet) used memory regions, implement a
set_memory_attr() function to:
- set the final memory protection after init on currently used
kernel regions.
- enable/disable kernel memory regions in the scope of DEBUG_PAGEALLOC.
Unlike the set_memory_xx() which can act in three step as the regions
are unused, this function must modify 'on the fly' as the kernel is
executing from them. At the moment only PPC32 will use it and changing
page attributes on the fly is not an issue.
Signed-off-by: Christophe Leroy <redacted>
Reported-by: kbuild test robot <redacted>
[ruscur: cast "data" to unsigned long instead of int]
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/include/asm/set_memory.h | 2 ++
arch/powerpc/mm/pageattr.c | 33 +++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
@@ -29,4 +29,6 @@ static inline int set_memory_x(unsigned long addr, int numpages)returnchange_memory_attr(addr,numpages,SET_MEMORY_X);}+intset_memory_attr(unsignedlongaddr,intnumpages,pgprot_tprot);+#endif
@@ -79,3 +79,36 @@ int change_memory_attr(unsigned long addr, int numpages, long action)returnapply_to_existing_page_range(&init_mm,start,sz,change_page_attr,(void*)action);}++/*+*Settheattributesofapage:+*+*ThisfunctionisusedbyPPC32attheendofinittosetfinalkernelmemory+*protection.Itincludeschangingthemapingofthepageitisexecutingfrom+*anddatapagesitisusing.+*/+staticintset_page_attr(pte_t*ptep,unsignedlongaddr,void*data)+{+pgprot_tprot=__pgprot((unsignedlong)data);++spin_lock(&init_mm.page_table_lock);++set_pte_at(&init_mm,addr,ptep,pte_modify(*ptep,prot));+flush_tlb_kernel_range(addr,addr+PAGE_SIZE);++spin_unlock(&init_mm.page_table_lock);++return0;+}++intset_memory_attr(unsignedlongaddr,intnumpages,pgprot_tprot)+{+unsignedlongstart=ALIGN_DOWN(addr,PAGE_SIZE);+unsignedlongsz=numpages*PAGE_SIZE;++if(numpages<=0)+return0;++returnapply_to_existing_page_range(&init_mm,start,sz,set_page_attr,+(void*)pgprot_val(prot));+}
From: Jordan Niethe <hidden> Date: 2021-03-16 03:22:01
From: Christophe Leroy <redacted>
Use set_memory_attr() instead of the PPC32 specific change_page_attr()
change_page_attr() was checking that the address was not mapped by
blocks and was handling highmem, but that's unneeded because the
affected pages can't be in highmem and block mapping verification
is already done by the callers.
Signed-off-by: Christophe Leroy <redacted>
[ruscur: rebase on powerpc/merge with Christophe's new patches]
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/mm/pgtable_32.c | 60 ++++++------------------------------
1 file changed, 10 insertions(+), 50 deletions(-)
@@ -198,20 +155,18 @@ void mark_rodata_ro(void)return;}-page=virt_to_page(_stext);numpages=PFN_UP((unsignedlong)_etext)-PFN_DOWN((unsignedlong)_stext);-change_page_attr(page,numpages,PAGE_KERNEL_ROX);+set_memory_attr((unsignedlong)_stext,numpages,PAGE_KERNEL_ROX);/**mark.rodataasreadonly.Use__init_beginratherthan__end_rodata*tocoverNOTESandEXCEPTION_TABLE.*/-page=virt_to_page(__start_rodata);numpages=PFN_UP((unsignedlong)__init_begin)-PFN_DOWN((unsignedlong)__start_rodata);-change_page_attr(page,numpages,PAGE_KERNEL_RO);+set_memory_attr((unsignedlong)__start_rodata,numpages,PAGE_KERNEL_RO);// mark_initmem_nx() should have already run by nowptdump_check_wx();
From: Russell Currey <hidden> Date: 2021-03-16 03:46:27
On Tue, 2021-03-16 at 14:17 +1100, Jordan Niethe wrote:
setup_text_poke_area() is a late init call so it runs before
mark_rodata_ro() and after the init calls. This lets all the init
code
patching simply write to their locations. In the future, kprobes is
going to allocate its instruction pages RO which means they will need
setup_text__poke_area() to have been already called for their code
patching. However, init_kprobes() (which allocates and patches some
instruction pages) is an early init call so it happens before
setup_text__poke_area().
start_kernel() calls poking_init() before any of the init calls. On
powerpc, poking_init() is currently a nop. setup_text_poke_area()
relies
on kernel virtual memory, cpu hotplug and per_cpu_areas being setup.
setup_per_cpu_areas(), boot_cpu_hotplug_init() and mm_init() are
called
before poking_init().
Turn setup_text_poke_area() into poking_init().
Signed-off-by: Jordan Niethe <redacted>
Good job finding & fixing this bug!
Reviewed-by: Russell Currey <redacted>
quoted hunk
---
v9: New to series
---
arch/powerpc/lib/code-patching.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/arch/powerpc/lib/code-patching.c
b/arch/powerpc/lib/code-patching.c
index 2333625b5e31..b28afa1133db 100644
@@ -65,14 +65,11 @@ static int text_area_cpu_down(unsigned int cpu)
}
/*
- * Run as a late init call. This allows all the boot time patching
to be done
- * simply by patching the code, and then we're called here prior to
- * mark_rodata_ro(), which happens after all init calls are run.
Although
- * BUG_ON() is rude, in this case it should only happen if ENOMEM,
and we judge
- * it as being preferable to a kernel that will crash later when
someone tries
- * to use patch_instruction().
+ * Although BUG_ON() is rude, in this case it should only happen if
ENOMEM, and
+ * we judge it as being preferable to a kernel that will crash later
when
+ * someone tries to use patch_instruction().
*/
-static int __init setup_text_poke_area(void)
+int __init poking_init(void)
{
BUG_ON(!cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"powerpc/text_poke:online", text_area_cpu_up,
@@ -80,7 +77,6 @@ static int __init setup_text_poke_area(void)
return 0;
}
-late_initcall(setup_text_poke_area);
/*
* This can be called for kernel text or a module.
setup_text_poke_area() is a late init call so it runs before
mark_rodata_ro() and after the init calls. This lets all the init code
patching simply write to their locations. In the future, kprobes is
going to allocate its instruction pages RO which means they will need
setup_text__poke_area() to have been already called for their code
patching. However, init_kprobes() (which allocates and patches some
instruction pages) is an early init call so it happens before
setup_text__poke_area().
start_kernel() calls poking_init() before any of the init calls. On
powerpc, poking_init() is currently a nop. setup_text_poke_area() relies
on kernel virtual memory, cpu hotplug and per_cpu_areas being setup.
setup_per_cpu_areas(), boot_cpu_hotplug_init() and mm_init() are called
before poking_init().
Turn setup_text_poke_area() into poking_init().
Signed-off-by: Jordan Niethe <redacted>
---
v9: New to series
---
arch/powerpc/lib/code-patching.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
@@ -65,14 +65,11 @@ static int text_area_cpu_down(unsigned int cpu)}/*-*Runasalateinitcall.Thisallowsalltheboottimepatchingtobedone-*simplybypatchingthecode,andthenwe'recalledherepriorto-*mark_rodata_ro(),whichhappensafterallinitcallsarerun.Although-*BUG_ON()isrude,inthiscaseitshouldonlyhappenifENOMEM,andwejudge-*itasbeingpreferabletoakernelthatwillcrashlaterwhensomeonetries-*tousepatch_instruction().+*AlthoughBUG_ON()isrude,inthiscaseitshouldonlyhappenifENOMEM,and+*wejudgeitasbeingpreferabletoakernelthatwillcrashlaterwhen+*someonetriestousepatch_instruction().
@@ -80,7 +77,6 @@ static int __init setup_text_poke_area(void) return 0; }-late_initcall(setup_text_poke_area); /* * This can be called for kernel text or a module.
From: Russell Currey <redacted>
With CONFIG_STRICT_KERNEL_RWX=y and CONFIG_KPROBES=y, there will be one
W+X page at boot by default. This can be tested with
CONFIG_PPC_PTDUMP=y and CONFIG_PPC_DEBUG_WX=y set, and checking the
kernel log during boot.
This text is confusing. I don't understand what is the status before the patch, and what is the
status after.
"there will be one ...", does it mean after the patch ?
Add an arch specific insn page allocator which returns RO pages if
STRICT_KERNEL_RWX is enabled. This page is only written to with
patch_instruction() which is able to write RO pages.
"an" or "the" arch specific insn page allocator ?
quoted hunk
Reviewed-by: Daniel Axtens <redacted>
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Christophe Leroy <redacted>
[jpn: Reword commit message, switch from vmalloc_exec(), add
free_insn_page()]
Signed-off-by: Jordan Niethe <redacted>
---
v9: - vmalloc_exec() no longer exists
- Set the page to RW before freeing it
---
arch/powerpc/kernel/kprobes.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
Can't do that on book3s/32, see https://github.com/linuxppc/linux/commit/6ca05532 and
https://github.com/linuxppc/linux/commit/7fbc22ce
Should do:
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, GFP_KERNEL,
PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS, NUMA_NO_NODE,
__builtin_return_address(0));
To keep it simple, you'll probably need to define MODULES_VADDR and MODULES_END as resp
VMALLOC_START and VMALLOC_END when they are not defined, maybe in asm/pgtable.h
From: Russell Currey <redacted>
Very rudimentary, just
echo 1 > [debugfs]/check_wx_pages
and check the kernel log. Useful for testing strict module RWX.
Updated the Kconfig entry to reflect this.
Also fixed a typo.
Why not just perform the test everytime someone dumps kernel_page_tables ?
@@ -371,7 +371,7 @@ config PPC_PTDUMPIfyouareunsure,sayN.configPPC_DEBUG_WX-bool"Warn on W+X mappings at boot"+bool"Warn on W+X mappings at boot & enable manual checks at runtime"depends onPPC_PTDUMP&&STRICT_KERNEL_RWXhelpGenerateawarningifanyW+Xmappingsarefoundatboot.
From: Russell Currey <redacted>
To enable strict module RWX on powerpc, set:
CONFIG_STRICT_MODULE_RWX=y
You should also have CONFIG_STRICT_KERNEL_RWX=y set to have any real
security benefit.
ARCH_HAS_STRICT_MODULE_RWX is set to require ARCH_HAS_STRICT_KERNEL_RWX.
This is due to a quirk in arch/Kconfig and arch/powerpc/Kconfig that
makes STRICT_MODULE_RWX *on by default* in configurations where
STRICT_KERNEL_RWX is *unavailable*.
Since this doesn't make much sense, and module RWX without kernel RWX
doesn't make much sense, having the same dependencies as kernel RWX
works around this problem.
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/Kconfig | 1 +
1 file changed, 1 insertion(+)
Can you please update the whole series with my new email address: christophe.leroy@csgroup.eu
quoted hunk
In addition to the set_memory_xx() functions which allows to change
the memory attributes of not (yet) used memory regions, implement a
set_memory_attr() function to:
- set the final memory protection after init on currently used
kernel regions.
- enable/disable kernel memory regions in the scope of DEBUG_PAGEALLOC.
Unlike the set_memory_xx() which can act in three step as the regions
are unused, this function must modify 'on the fly' as the kernel is
executing from them. At the moment only PPC32 will use it and changing
page attributes on the fly is not an issue.
Signed-off-by: Christophe Leroy <redacted>
Reported-by: kbuild test robot <redacted>
[ruscur: cast "data" to unsigned long instead of int]
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/include/asm/set_memory.h | 2 ++
arch/powerpc/mm/pageattr.c | 33 +++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
@@ -29,4 +29,6 @@ static inline int set_memory_x(unsigned long addr, int numpages)returnchange_memory_attr(addr,numpages,SET_MEMORY_X);}+intset_memory_attr(unsignedlongaddr,intnumpages,pgprot_tprot);+#endif
@@ -79,3 +79,36 @@ int change_memory_attr(unsigned long addr, int numpages, long action)returnapply_to_existing_page_range(&init_mm,start,sz,change_page_attr,(void*)action);}++/*+*Settheattributesofapage:+*+*ThisfunctionisusedbyPPC32attheendofinittosetfinalkernelmemory+*protection.Itincludeschangingthemapingofthepageitisexecutingfrom+*anddatapagesitisusing.+*/+staticintset_page_attr(pte_t*ptep,unsignedlongaddr,void*data)+{+pgprot_tprot=__pgprot((unsignedlong)data);++spin_lock(&init_mm.page_table_lock);++set_pte_at(&init_mm,addr,ptep,pte_modify(*ptep,prot));+flush_tlb_kernel_range(addr,addr+PAGE_SIZE);++spin_unlock(&init_mm.page_table_lock);++return0;+}++intset_memory_attr(unsignedlongaddr,intnumpages,pgprot_tprot)+{+unsignedlongstart=ALIGN_DOWN(addr,PAGE_SIZE);+unsignedlongsz=numpages*PAGE_SIZE;++if(numpages<=0)+return0;++returnapply_to_existing_page_range(&init_mm,start,sz,set_page_attr,+(void*)pgprot_val(prot));+}
From: Jordan Niethe <hidden> Date: 2021-03-17 00:39:36
On Tue, Mar 16, 2021 at 5:32 PM Christophe Leroy
[off-list ref] wrote:
Le 16/03/2021 à 04:17, Jordan Niethe a écrit :
quoted
setup_text_poke_area() is a late init call so it runs before
mark_rodata_ro() and after the init calls. This lets all the init code
patching simply write to their locations. In the future, kprobes is
going to allocate its instruction pages RO which means they will need
setup_text__poke_area() to have been already called for their code
patching. However, init_kprobes() (which allocates and patches some
instruction pages) is an early init call so it happens before
setup_text__poke_area().
start_kernel() calls poking_init() before any of the init calls. On
powerpc, poking_init() is currently a nop. setup_text_poke_area() relies
on kernel virtual memory, cpu hotplug and per_cpu_areas being setup.
setup_per_cpu_areas(), boot_cpu_hotplug_init() and mm_init() are called
before poking_init().
Turn setup_text_poke_area() into poking_init().
Signed-off-by: Jordan Niethe <redacted>
---
v9: New to series
---
arch/powerpc/lib/code-patching.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
@@ -65,14 +65,11 @@ static int text_area_cpu_down(unsigned int cpu)}/*-*Runasalateinitcall.Thisallowsalltheboottimepatchingtobedone-*simplybypatchingthecode,andthenwe'recalledherepriorto-*mark_rodata_ro(),whichhappensafterallinitcallsarerun.Although-*BUG_ON()isrude,inthiscaseitshouldonlyhappenifENOMEM,andwejudge-*itasbeingpreferabletoakernelthatwillcrashlaterwhensomeonetries-*tousepatch_instruction().+*AlthoughBUG_ON()isrude,inthiscaseitshouldonlyhappenifENOMEM,and+*wejudgeitasbeingpreferabletoakernelthatwillcrashlaterwhen+*someonetriestousepatch_instruction().
@@ -80,7 +77,6 @@ static int __init setup_text_poke_area(void) return 0; }-late_initcall(setup_text_poke_area); /* * This can be called for kernel text or a module.
From: Jordan Niethe <hidden> Date: 2021-03-17 00:51:06
On Tue, Mar 16, 2021 at 5:44 PM Christophe Leroy
[off-list ref] wrote:
Le 16/03/2021 à 04:17, Jordan Niethe a écrit :
quoted
From: Russell Currey <redacted>
With CONFIG_STRICT_KERNEL_RWX=y and CONFIG_KPROBES=y, there will be one
W+X page at boot by default. This can be tested with
CONFIG_PPC_PTDUMP=y and CONFIG_PPC_DEBUG_WX=y set, and checking the
kernel log during boot.
This text is confusing. I don't understand what is the status before the patch, and what is the
status after.
Before the patch kprobes is allocating W+X pages. This can be seen in
the kernel log with those debug options on.
After the patch kprobes no longer allocate W+X pages.
I will reword it to more clear.
"there will be one ...", does it mean after the patch ?
No, before, after there will be none.
quoted
Add an arch specific insn page allocator which returns RO pages if
STRICT_KERNEL_RWX is enabled. This page is only written to with
patch_instruction() which is able to write RO pages.
"an" or "the" arch specific insn page allocator ?
Hmm, will go with "the arch specific insn page allocator for powerpc".
quoted
Reviewed-by: Daniel Axtens <redacted>
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Christophe Leroy <redacted>
[jpn: Reword commit message, switch from vmalloc_exec(), add
free_insn_page()]
Signed-off-by: Jordan Niethe <redacted>
---
v9: - vmalloc_exec() no longer exists
- Set the page to RW before freeing it
---
arch/powerpc/kernel/kprobes.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
Can't do that on book3s/32, see https://github.com/linuxppc/linux/commit/6ca05532 and
https://github.com/linuxppc/linux/commit/7fbc22ce
Should do:
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, GFP_KERNEL,
PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS, NUMA_NO_NODE,
__builtin_return_address(0));
To keep it simple, you'll probably need to define MODULES_VADDR and MODULES_END as resp
VMALLOC_START and VMALLOC_END when they are not defined, maybe in asm/pgtable.h
From: Jordan Niethe <hidden> Date: 2021-03-17 00:53:19
On Tue, Mar 16, 2021 at 5:44 PM Christophe Leroy
[off-list ref] wrote:
Le 16/03/2021 à 04:17, Jordan Niethe a écrit :
quoted
From: Russell Currey <redacted>
With CONFIG_STRICT_KERNEL_RWX=y and CONFIG_KPROBES=y, there will be one
W+X page at boot by default. This can be tested with
CONFIG_PPC_PTDUMP=y and CONFIG_PPC_DEBUG_WX=y set, and checking the
kernel log during boot.
This text is confusing. I don't understand what is the status before the patch, and what is the
status after.
"there will be one ...", does it mean after the patch ?
quoted
Add an arch specific insn page allocator which returns RO pages if
STRICT_KERNEL_RWX is enabled. This page is only written to with
patch_instruction() which is able to write RO pages.
"an" or "the" arch specific insn page allocator ?
quoted
Reviewed-by: Daniel Axtens <redacted>
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Christophe Leroy <redacted>
[jpn: Reword commit message, switch from vmalloc_exec(), add
free_insn_page()]
Signed-off-by: Jordan Niethe <redacted>
---
v9: - vmalloc_exec() no longer exists
- Set the page to RW before freeing it
---
arch/powerpc/kernel/kprobes.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
Can't do that on book3s/32, see https://github.com/linuxppc/linux/commit/6ca05532 and
https://github.com/linuxppc/linux/commit/7fbc22ce
Should do:
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, GFP_KERNEL,
PAGE_KERNEL_ROX, VM_FLUSH_RESET_PERMS, NUMA_NO_NODE,
__builtin_return_address(0));
To keep it simple, you'll probably need to define MODULES_VADDR and MODULES_END as resp
VMALLOC_START and VMALLOC_END when they are not defined, maybe in asm/pgtable.h
Thank you, I had overlooked that. I will do it like that in the next revision.
From: Jordan Niethe <hidden> Date: 2021-03-17 00:55:07
On Tue, Mar 16, 2021 at 6:25 PM Christophe Leroy
[off-list ref] wrote:
Le 16/03/2021 à 04:17, Jordan Niethe a écrit :
quoted
From: Christophe Leroy <redacted>
Can you please update the whole series with my new email address: christophe.leroy@csgroup.eu
Of course, I shall do that.
quoted
In addition to the set_memory_xx() functions which allows to change
the memory attributes of not (yet) used memory regions, implement a
set_memory_attr() function to:
- set the final memory protection after init on currently used
kernel regions.
- enable/disable kernel memory regions in the scope of DEBUG_PAGEALLOC.
Unlike the set_memory_xx() which can act in three step as the regions
are unused, this function must modify 'on the fly' as the kernel is
executing from them. At the moment only PPC32 will use it and changing
page attributes on the fly is not an issue.
Signed-off-by: Christophe Leroy <redacted>
Reported-by: kbuild test robot <redacted>
[ruscur: cast "data" to unsigned long instead of int]
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/include/asm/set_memory.h | 2 ++
arch/powerpc/mm/pageattr.c | 33 +++++++++++++++++++++++++++
2 files changed, 35 insertions(+)
@@ -29,4 +29,6 @@ static inline int set_memory_x(unsigned long addr, int numpages)returnchange_memory_attr(addr,numpages,SET_MEMORY_X);}+intset_memory_attr(unsignedlongaddr,intnumpages,pgprot_tprot);+#endif
@@ -79,3 +79,36 @@ int change_memory_attr(unsigned long addr, int numpages, long action)returnapply_to_existing_page_range(&init_mm,start,sz,change_page_attr,(void*)action);}++/*+*Settheattributesofapage:+*+*ThisfunctionisusedbyPPC32attheendofinittosetfinalkernelmemory+*protection.Itincludeschangingthemapingofthepageitisexecutingfrom+*anddatapagesitisusing.+*/+staticintset_page_attr(pte_t*ptep,unsignedlongaddr,void*data)+{+pgprot_tprot=__pgprot((unsignedlong)data);++spin_lock(&init_mm.page_table_lock);++set_pte_at(&init_mm,addr,ptep,pte_modify(*ptep,prot));+flush_tlb_kernel_range(addr,addr+PAGE_SIZE);++spin_unlock(&init_mm.page_table_lock);++return0;+}++intset_memory_attr(unsignedlongaddr,intnumpages,pgprot_tprot)+{+unsignedlongstart=ALIGN_DOWN(addr,PAGE_SIZE);+unsignedlongsz=numpages*PAGE_SIZE;++if(numpages<=0)+return0;++returnapply_to_existing_page_range(&init_mm,start,sz,set_page_attr,+(void*)pgprot_val(prot));+}
From: Jordan Niethe <hidden> Date: 2021-03-17 02:16:29
On Tue, Mar 16, 2021 at 5:51 PM Christophe Leroy
[off-list ref] wrote:
Le 16/03/2021 à 04:17, Jordan Niethe a écrit :
quoted
From: Russell Currey <redacted>
To enable strict module RWX on powerpc, set:
CONFIG_STRICT_MODULE_RWX=y
You should also have CONFIG_STRICT_KERNEL_RWX=y set to have any real
security benefit.
ARCH_HAS_STRICT_MODULE_RWX is set to require ARCH_HAS_STRICT_KERNEL_RWX.
This is due to a quirk in arch/Kconfig and arch/powerpc/Kconfig that
makes STRICT_MODULE_RWX *on by default* in configurations where
STRICT_KERNEL_RWX is *unavailable*.
Would it be ok to just make ARCH_HAS_STRICT_MODULE_RWX conditional on 64s?
quoted
Since this doesn't make much sense, and module RWX without kernel RWX
doesn't make much sense, having the same dependencies as kernel RWX
works around this problem.
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/Kconfig | 1 +
1 file changed, 1 insertion(+)
On Tue, Mar 16, 2021 at 5:51 PM Christophe Leroy
[off-list ref] wrote:
quoted
Le 16/03/2021 à 04:17, Jordan Niethe a écrit :
quoted
From: Russell Currey <redacted>
To enable strict module RWX on powerpc, set:
CONFIG_STRICT_MODULE_RWX=y
You should also have CONFIG_STRICT_KERNEL_RWX=y set to have any real
security benefit.
ARCH_HAS_STRICT_MODULE_RWX is set to require ARCH_HAS_STRICT_KERNEL_RWX.
This is due to a quirk in arch/Kconfig and arch/powerpc/Kconfig that
makes STRICT_MODULE_RWX *on by default* in configurations where
STRICT_KERNEL_RWX is *unavailable*.
Would it be ok to just make ARCH_HAS_STRICT_MODULE_RWX conditional on 64s?
I think making it conditional on !PPC_BOOK3S_604 should be enough.
quoted
quoted
Since this doesn't make much sense, and module RWX without kernel RWX
doesn't make much sense, having the same dependencies as kernel RWX
works around this problem.
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/Kconfig | 1 +
1 file changed, 1 insertion(+)
From: Russell Currey <redacted>
With CONFIG_STRICT_KERNEL_RWX=y and CONFIG_KPROBES=y, there will be one
W+X page at boot by default. This can be tested with
CONFIG_PPC_PTDUMP=y and CONFIG_PPC_DEBUG_WX=y set, and checking the
kernel log during boot.
Add an arch specific insn page allocator which returns RO pages if
STRICT_KERNEL_RWX is enabled. This page is only written to with
patch_instruction() which is able to write RO pages.
Did you investigate BPF ? The problematic looks more or less similar to kprobe:
bpf_jit_compile() in arch/powerpc/net/bpf_jit_comp.c calls module_alloc(), which provides it with
PAGE_KERNEL_TEXT memory, ie RWX. That function is only used on PPC32 which still has Classic BPF,
and this is about to go away with future series
https://patchwork.ozlabs.org/project/linuxppc-dev/cover/cover.1608112796.git.christophe.leroy@csgroup.eu/
PPC64 has Extended BPF instead, and PPC32 will it the future too.
bpf_int_jit_compile() in arch/powerpc/net/bpf_jit_comp64.c calls bpf_jit_binary_alloc() which uses
bpf_jit_alloc_exec().
bpf_jit_alloc_exec() is a weak function that should be redefined for powerpc I think, more or less
like alloc_insn_page() for kprobes.
Christophe
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2021-03-17 12:05:10
Jordan Niethe [off-list ref] writes:
On Tue, Mar 16, 2021 at 5:32 PM Christophe Leroy
[off-list ref] wrote:
quoted
Le 16/03/2021 à 04:17, Jordan Niethe a écrit :
quoted
setup_text_poke_area() is a late init call so it runs before
mark_rodata_ro() and after the init calls. This lets all the init code
patching simply write to their locations. In the future, kprobes is
going to allocate its instruction pages RO which means they will need
setup_text__poke_area() to have been already called for their code
patching. However, init_kprobes() (which allocates and patches some
instruction pages) is an early init call so it happens before
setup_text__poke_area().
start_kernel() calls poking_init() before any of the init calls. On
powerpc, poking_init() is currently a nop. setup_text_poke_area() relies
on kernel virtual memory, cpu hotplug and per_cpu_areas being setup.
setup_per_cpu_areas(), boot_cpu_hotplug_init() and mm_init() are called
before poking_init().
Turn setup_text_poke_area() into poking_init().
Signed-off-by: Jordan Niethe <redacted>
---
v9: New to series
---
arch/powerpc/lib/code-patching.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
@@ -65,14 +65,11 @@ static int text_area_cpu_down(unsigned int cpu)}/*-*Runasalateinitcall.Thisallowsalltheboottimepatchingtobedone-*simplybypatchingthecode,andthenwe'recalledherepriorto-*mark_rodata_ro(),whichhappensafterallinitcallsarerun.Although-*BUG_ON()isrude,inthiscaseitshouldonlyhappenifENOMEM,andwejudge-*itasbeingpreferabletoakernelthatwillcrashlaterwhensomeonetries-*tousepatch_instruction().+*AlthoughBUG_ON()isrude,inthiscaseitshouldonlyhappenifENOMEM,and+*wejudgeitasbeingpreferabletoakernelthatwillcrashlaterwhen+*someonetriestousepatch_instruction().
Ok I can include a change to WARN_ON() as a separate patch.
I'm not convinced we should change this to a WARN_ON.
Being able to patch the kernel text is not optional.
Patching jump labels has no ability to return an error, and the code
that uses them has no concept of the jump label not taking the correct
polarity.
Silently failing the patch is like randomly flipping an if condition
somewhere in the kernel and hoping that everything will continue
working.
cheers
From: Jordan Niethe <hidden> Date: 2021-03-18 02:42:47
On Wed, Mar 17, 2021 at 5:12 PM Christophe Leroy
[off-list ref] wrote:
Le 16/03/2021 à 04:17, Jordan Niethe a écrit :
quoted
From: Russell Currey <redacted>
With CONFIG_STRICT_KERNEL_RWX=y and CONFIG_KPROBES=y, there will be one
W+X page at boot by default. This can be tested with
CONFIG_PPC_PTDUMP=y and CONFIG_PPC_DEBUG_WX=y set, and checking the
kernel log during boot.
Add an arch specific insn page allocator which returns RO pages if
STRICT_KERNEL_RWX is enabled. This page is only written to with
patch_instruction() which is able to write RO pages.
Did you investigate BPF ? The problematic looks more or less similar to kprobe:
bpf_jit_compile() in arch/powerpc/net/bpf_jit_comp.c calls module_alloc(), which provides it with
PAGE_KERNEL_TEXT memory, ie RWX. That function is only used on PPC32 which still has Classic BPF,
and this is about to go away with future series
https://patchwork.ozlabs.org/project/linuxppc-dev/cover/cover.1608112796.git.christophe.leroy@csgroup.eu/
PPC64 has Extended BPF instead, and PPC32 will it the future too.
bpf_int_jit_compile() in arch/powerpc/net/bpf_jit_comp64.c calls bpf_jit_binary_alloc() which uses
bpf_jit_alloc_exec().
bpf_jit_alloc_exec() is a weak function that should be redefined for powerpc I think, more or less
like alloc_insn_page() for kprobes.
Thanks, that is a good point. I will handle bpf with the next revision.
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2021-03-19 01:19:46
Jordan Niethe [off-list ref] writes:
From: Russell Currey <redacted>
The set_memory_{ro/rw/nx/x}() functions are required for STRICT_MODULE_RWX,
and are generally useful primitives to have. This implementation is
designed to be completely generic across powerpc's many MMUs.
It's possible that this could be optimised to be faster for specific
MMUs, but the focus is on having a generic and safe implementation for
now.
This won't work for the linear mapping with HPT on book3s 64. Because
the linear mapping is not in the kernel page tables.
apply_to_existing_page_range() should work that out and return an error.
But I'm not sure if callers handle that well or at all.
We might want to add a WARN_ON_ONCE() in change_memory_attr(), at least
to begin with, to report those errors, so we know when we are failing to
set permissions. Rather than silently failing and then crashing some
time later due to the permissions being wrong for some mapping.
cheers
quoted hunk
This implementation does not handle cases where the caller is attempting
to change the mapping of the page it is executing from, or if another
CPU is concurrently using the page being altered. These cases likely
shouldn't happen, but a more complex implementation with MMU-specific code
could safely handle them, so that is left as a TODO for now.
These functions do nothing if STRICT_KERNEL_RWX is not enabled.
Reviewed-by: Daniel Axtens <redacted>
Signed-off-by: Russell Currey <redacted>
Signed-off-by: Christophe Leroy <redacted>
[jpn: rebase on next plus "powerpc/mm/64s: Allow STRICT_KERNEL_RWX again"]
Signed-off-by: Jordan Niethe <redacted>
---
arch/powerpc/Kconfig | 1 +
arch/powerpc/include/asm/set_memory.h | 32 +++++++++++
arch/powerpc/mm/Makefile | 2 +-
arch/powerpc/mm/pageattr.c | 81 +++++++++++++++++++++++++++
4 files changed, 115 insertions(+), 1 deletion(-)
create mode 100644 arch/powerpc/include/asm/set_memory.h
create mode 100644 arch/powerpc/mm/pageattr.c
@@ -0,0 +1,81 @@+// SPDX-License-Identifier: GPL-2.0++/*+*MMU-genericset_memoryimplementationforpowerpc+*+*Copyright2019,IBMCorporation.+*/++#include<linux/mm.h>+#include<linux/set_memory.h>++#include<asm/mmu.h>+#include<asm/page.h>+#include<asm/pgtable.h>+++/*+*Updatestheattributesofapageinthreesteps:+*+*1.invalidatethepagetableentry+*2.flushtheTLB+*3.installthenewentrywiththeupdatedattributes+*+*Thisisunsafeifthecallerisattemptingtochangethemappingofthe+*pageitisexecutingfrom,orifanotherCPUisconcurrentlyusingthe+*pagebeingaltered.+*+*TODOmaketheimplementationresistanttothis.+*+*NOTE:canbedangeroustocallwithoutSTRICT_KERNEL_RWX+*/+staticintchange_page_attr(pte_t*ptep,unsignedlongaddr,void*data)+{+longaction=(long)data;+pte_tpte;++spin_lock(&init_mm.page_table_lock);++/* invalidate the PTE so it's safe to modify */+pte=ptep_get_and_clear(&init_mm,addr,ptep);+flush_tlb_kernel_range(addr,addr+PAGE_SIZE);++/* modify the PTE bits as desired, then apply */+switch(action){+caseSET_MEMORY_RO:+pte=pte_wrprotect(pte);+break;+caseSET_MEMORY_RW:+pte=pte_mkwrite(pte);+break;+caseSET_MEMORY_NX:+pte=pte_exprotect(pte);+break;+caseSET_MEMORY_X:+pte=pte_mkexec(pte);+break;+default:+WARN_ON_ONCE(1);+break;+}++set_pte_at(&init_mm,addr,ptep,pte);+spin_unlock(&init_mm.page_table_lock);++return0;+}++intchange_memory_attr(unsignedlongaddr,intnumpages,longaction)+{+unsignedlongstart=ALIGN_DOWN(addr,PAGE_SIZE);+unsignedlongsz=numpages*PAGE_SIZE;++if(!IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))+return0;++if(numpages<=0)+return0;++returnapply_to_existing_page_range(&init_mm,start,sz,+change_page_attr,(void*)action);+}