aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm/vmem.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2016-03-22 10:54:24 +0100
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2017-02-08 14:13:25 +0100
commit57d7f939e7bdd746992f5c318a78697ba837c523 (patch)
tree80c01c9c446fc190432798a6d1ecf0c8e5456b57 /arch/s390/mm/vmem.c
parents390: report new vector facilities (diff)
downloadlinux-dev-57d7f939e7bdd746992f5c318a78697ba837c523.tar.xz
linux-dev-57d7f939e7bdd746992f5c318a78697ba837c523.zip
s390: add no-execute support
Bit 0x100 of a page table, segment table of region table entry can be used to disallow code execution for the virtual addresses associated with the entry. There is one tricky bit, the system call to return from a signal is part of the signal frame written to the user stack. With a non-executable stack this would stop working. To avoid breaking things the protection fault handler checks the opcode that caused the fault for 0x0a77 (sys_sigreturn) and 0x0aad (sys_rt_sigreturn) and injects a system call. This is preferable to the alternative solution with a stub function in the vdso because it works for vdso=off and statically linked binaries as well. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/mm/vmem.c')
-rw-r--r--arch/s390/mm/vmem.c42
1 files changed, 32 insertions, 10 deletions
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index 45becc8a44ec..253046344b3c 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -79,6 +79,7 @@ pte_t __ref *vmem_pte_alloc(void)
*/
static int vmem_add_mem(unsigned long start, unsigned long size)
{
+ unsigned long pgt_prot, sgt_prot, r3_prot;
unsigned long pages4k, pages1m, pages2g;
unsigned long end = start + size;
unsigned long address = start;
@@ -88,6 +89,14 @@ static int vmem_add_mem(unsigned long start, unsigned long size)
pte_t *pt_dir;
int ret = -ENOMEM;
+ pgt_prot = pgprot_val(PAGE_KERNEL);
+ sgt_prot = pgprot_val(SEGMENT_KERNEL);
+ r3_prot = pgprot_val(REGION3_KERNEL);
+ if (!MACHINE_HAS_NX) {
+ pgt_prot &= ~_PAGE_NOEXEC;
+ sgt_prot &= ~_SEGMENT_ENTRY_NOEXEC;
+ r3_prot &= ~_REGION_ENTRY_NOEXEC;
+ }
pages4k = pages1m = pages2g = 0;
while (address < end) {
pg_dir = pgd_offset_k(address);
@@ -101,7 +110,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size)
if (MACHINE_HAS_EDAT2 && pud_none(*pu_dir) && address &&
!(address & ~PUD_MASK) && (address + PUD_SIZE <= end) &&
!debug_pagealloc_enabled()) {
- pud_val(*pu_dir) = address | pgprot_val(REGION3_KERNEL);
+ pud_val(*pu_dir) = address | r3_prot;
address += PUD_SIZE;
pages2g++;
continue;
@@ -116,7 +125,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size)
if (MACHINE_HAS_EDAT1 && pmd_none(*pm_dir) && address &&
!(address & ~PMD_MASK) && (address + PMD_SIZE <= end) &&
!debug_pagealloc_enabled()) {
- pmd_val(*pm_dir) = address | pgprot_val(SEGMENT_KERNEL);
+ pmd_val(*pm_dir) = address | sgt_prot;
address += PMD_SIZE;
pages1m++;
continue;
@@ -129,7 +138,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size)
}
pt_dir = pte_offset_kernel(pm_dir, address);
- pte_val(*pt_dir) = address | pgprot_val(PAGE_KERNEL);
+ pte_val(*pt_dir) = address | pgt_prot;
address += PAGE_SIZE;
pages4k++;
}
@@ -200,6 +209,7 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
*/
int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
{
+ unsigned long pgt_prot, sgt_prot;
unsigned long address = start;
pgd_t *pg_dir;
pud_t *pu_dir;
@@ -207,6 +217,12 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
pte_t *pt_dir;
int ret = -ENOMEM;
+ pgt_prot = pgprot_val(PAGE_KERNEL);
+ sgt_prot = pgprot_val(SEGMENT_KERNEL);
+ if (!MACHINE_HAS_NX) {
+ pgt_prot &= ~_PAGE_NOEXEC;
+ sgt_prot &= ~_SEGMENT_ENTRY_NOEXEC;
+ }
for (address = start; address < end;) {
pg_dir = pgd_offset_k(address);
if (pgd_none(*pg_dir)) {
@@ -238,8 +254,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
new_page = vmemmap_alloc_block(PMD_SIZE, node);
if (!new_page)
goto out;
- pmd_val(*pm_dir) = __pa(new_page) |
- _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE;
+ pmd_val(*pm_dir) = __pa(new_page) | sgt_prot;
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
@@ -259,8 +274,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)
new_page = vmemmap_alloc_block(PAGE_SIZE, node);
if (!new_page)
goto out;
- pte_val(*pt_dir) =
- __pa(new_page) | pgprot_val(PAGE_KERNEL);
+ pte_val(*pt_dir) = __pa(new_page) | pgt_prot;
}
address += PAGE_SIZE;
}
@@ -372,13 +386,21 @@ out:
*/
void __init vmem_map_init(void)
{
- unsigned long size = _eshared - _stext;
struct memblock_region *reg;
for_each_memblock(memory, reg)
vmem_add_mem(reg->base, reg->size);
- set_memory_ro((unsigned long)_stext, size >> PAGE_SHIFT);
- pr_info("Write protected kernel read-only data: %luk\n", size >> 10);
+ __set_memory((unsigned long) _stext,
+ (_etext - _stext) >> PAGE_SHIFT,
+ SET_MEMORY_RO | SET_MEMORY_X);
+ __set_memory((unsigned long) _etext,
+ (_eshared - _etext) >> PAGE_SHIFT,
+ SET_MEMORY_RO);
+ __set_memory((unsigned long) _sinittext,
+ (_einittext - _sinittext) >> PAGE_SHIFT,
+ SET_MEMORY_RO | SET_MEMORY_X);
+ pr_info("Write protected kernel read-only data: %luk\n",
+ (_eshared - _stext) >> 10);
}
/*