Skip to content

Commit f8107a8

Browse files
hcahcaAlexander Gordeev
authored andcommitted
s390/mm: Simplify noexec page protection handling
By default page protection definitions like PAGE_RX have the _PAGE_NOEXEC bit set. For older machines without the instruction execution protection facility this bit is not allowed to be used in page table entries, and therefore must be removed. This is done at a couple of page table walkers, but also at some but not all page table modification functions like ptep_modify_prot_commit(). Avoid all of this and change the page, segment and region3 protection definitions so that the noexec bit is masked out automatically if the instruction execution-protection facility is not available. This is similar to what also various other architectures do which had to solve the same problem. Reviewed-by: Gerald Schaefer <gerald.schaefer@linux.ibm.com> Acked-by: Alexander Gordeev <agordeev@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
1 parent db449b1 commit f8107a8

10 files changed

Lines changed: 106 additions & 77 deletions

File tree

arch/s390/boot/boot.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
struct machine_info {
1414
unsigned char has_edat1 : 1;
1515
unsigned char has_edat2 : 1;
16-
unsigned char has_nx : 1;
1716
};
1817

1918
struct vmlinux_info {

arch/s390/boot/startup.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ unsigned long __bootdata_preserved(vmemmap_size);
3030
unsigned long __bootdata_preserved(MODULES_VADDR);
3131
unsigned long __bootdata_preserved(MODULES_END);
3232
unsigned long __bootdata_preserved(max_mappable);
33+
unsigned long __bootdata_preserved(page_noexec_mask);
34+
unsigned long __bootdata_preserved(segment_noexec_mask);
35+
unsigned long __bootdata_preserved(region_noexec_mask);
3336
int __bootdata_preserved(relocate_lowcore);
3437

3538
u64 __bootdata_preserved(stfle_fac_list[16]);
@@ -51,8 +54,14 @@ static void detect_facilities(void)
5154
}
5255
if (test_facility(78))
5356
machine.has_edat2 = 1;
54-
if (test_facility(130))
55-
machine.has_nx = 1;
57+
page_noexec_mask = -1UL;
58+
segment_noexec_mask = -1UL;
59+
region_noexec_mask = -1UL;
60+
if (!test_facility(130)) {
61+
page_noexec_mask &= ~_PAGE_NOEXEC;
62+
segment_noexec_mask &= ~_SEGMENT_ENTRY_NOEXEC;
63+
region_noexec_mask &= ~_REGION_ENTRY_NOEXEC;
64+
}
5665
}
5766

5867
static int cmma_test_essa(void)

arch/s390/boot/vmem.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ static void kasan_populate_shadow(unsigned long kernel_start, unsigned long kern
6767
int i;
6868

6969
pte_z = __pte(__pa(kasan_early_shadow_page) | pgprot_val(PAGE_KERNEL_RO));
70-
if (!machine.has_nx)
71-
pte_z = clear_pte_bit(pte_z, __pgprot(_PAGE_NOEXEC));
7270
crst_table_init((unsigned long *)kasan_early_shadow_p4d, p4d_val(p4d_z));
7371
crst_table_init((unsigned long *)kasan_early_shadow_pud, pud_val(pud_z));
7472
crst_table_init((unsigned long *)kasan_early_shadow_pmd, pmd_val(pmd_z));
@@ -294,8 +292,6 @@ static void pgtable_pte_populate(pmd_t *pmd, unsigned long addr, unsigned long e
294292
continue;
295293
entry = __pte(_pa(addr, PAGE_SIZE, mode));
296294
entry = set_pte_bit(entry, PAGE_KERNEL);
297-
if (!machine.has_nx)
298-
entry = clear_pte_bit(entry, __pgprot(_PAGE_NOEXEC));
299295
set_pte(pte, entry);
300296
pages++;
301297
}
@@ -320,8 +316,6 @@ static void pgtable_pmd_populate(pud_t *pud, unsigned long addr, unsigned long e
320316
if (can_large_pmd(pmd, addr, next, mode)) {
321317
entry = __pmd(_pa(addr, _SEGMENT_SIZE, mode));
322318
entry = set_pmd_bit(entry, SEGMENT_KERNEL);
323-
if (!machine.has_nx)
324-
entry = clear_pmd_bit(entry, __pgprot(_SEGMENT_ENTRY_NOEXEC));
325319
set_pmd(pmd, entry);
326320
pages++;
327321
continue;
@@ -353,8 +347,6 @@ static void pgtable_pud_populate(p4d_t *p4d, unsigned long addr, unsigned long e
353347
if (can_large_pud(pud, addr, next, mode)) {
354348
entry = __pud(_pa(addr, _REGION3_SIZE, mode));
355349
entry = set_pud_bit(entry, REGION3_KERNEL);
356-
if (!machine.has_nx)
357-
entry = clear_pud_bit(entry, __pgprot(_REGION_ENTRY_NOEXEC));
358350
set_pud(pud, entry);
359351
pages++;
360352
continue;

arch/s390/include/asm/pgtable.h

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ static inline int is_module_addr(void *addr)
124124
#define KASLR_LEN 0UL
125125
#endif
126126

127+
void setup_protection_map(void);
128+
127129
/*
128130
* A 64 bit pagetable entry of S390 has following format:
129131
* | PFRA |0IPC| OS |
@@ -442,76 +444,107 @@ static inline int is_module_addr(void *addr)
442444
/*
443445
* Page protection definitions.
444446
*/
445-
#define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_INVALID | _PAGE_PROTECT)
446-
#define PAGE_RO __pgprot(_PAGE_PRESENT | _PAGE_READ | \
447+
#define __PAGE_NONE (_PAGE_PRESENT | _PAGE_INVALID | _PAGE_PROTECT)
448+
#define __PAGE_RO (_PAGE_PRESENT | _PAGE_READ | \
447449
_PAGE_NOEXEC | _PAGE_INVALID | _PAGE_PROTECT)
448-
#define PAGE_RX __pgprot(_PAGE_PRESENT | _PAGE_READ | \
450+
#define __PAGE_RX (_PAGE_PRESENT | _PAGE_READ | \
449451
_PAGE_INVALID | _PAGE_PROTECT)
450-
#define PAGE_RW __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
452+
#define __PAGE_RW (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
451453
_PAGE_NOEXEC | _PAGE_INVALID | _PAGE_PROTECT)
452-
#define PAGE_RWX __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
454+
#define __PAGE_RWX (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
453455
_PAGE_INVALID | _PAGE_PROTECT)
454-
455-
#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
456+
#define __PAGE_SHARED (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
456457
_PAGE_YOUNG | _PAGE_DIRTY | _PAGE_NOEXEC)
457-
#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
458+
#define __PAGE_KERNEL (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
458459
_PAGE_YOUNG | _PAGE_DIRTY | _PAGE_NOEXEC)
459-
#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_YOUNG | \
460+
#define __PAGE_KERNEL_RO (_PAGE_PRESENT | _PAGE_READ | _PAGE_YOUNG | \
460461
_PAGE_PROTECT | _PAGE_NOEXEC)
461462

463+
extern unsigned long page_noexec_mask;
464+
465+
#define __pgprot_page_mask(x) __pgprot((x) & page_noexec_mask)
466+
467+
#define PAGE_NONE __pgprot_page_mask(__PAGE_NONE)
468+
#define PAGE_RO __pgprot_page_mask(__PAGE_RO)
469+
#define PAGE_RX __pgprot_page_mask(__PAGE_RX)
470+
#define PAGE_RW __pgprot_page_mask(__PAGE_RW)
471+
#define PAGE_RWX __pgprot_page_mask(__PAGE_RWX)
472+
#define PAGE_SHARED __pgprot_page_mask(__PAGE_SHARED)
473+
#define PAGE_KERNEL __pgprot_page_mask(__PAGE_KERNEL)
474+
#define PAGE_KERNEL_RO __pgprot_page_mask(__PAGE_KERNEL_RO)
475+
462476
/*
463477
* Segment entry (large page) protection definitions.
464478
*/
465-
#define SEGMENT_NONE __pgprot(_SEGMENT_ENTRY_PRESENT | \
479+
#define __SEGMENT_NONE (_SEGMENT_ENTRY_PRESENT | \
466480
_SEGMENT_ENTRY_INVALID | \
467481
_SEGMENT_ENTRY_PROTECT)
468-
#define SEGMENT_RO __pgprot(_SEGMENT_ENTRY_PRESENT | \
482+
#define __SEGMENT_RO (_SEGMENT_ENTRY_PRESENT | \
469483
_SEGMENT_ENTRY_PROTECT | \
470484
_SEGMENT_ENTRY_READ | \
471485
_SEGMENT_ENTRY_NOEXEC)
472-
#define SEGMENT_RX __pgprot(_SEGMENT_ENTRY_PRESENT | \
486+
#define __SEGMENT_RX (_SEGMENT_ENTRY_PRESENT | \
473487
_SEGMENT_ENTRY_PROTECT | \
474488
_SEGMENT_ENTRY_READ)
475-
#define SEGMENT_RW __pgprot(_SEGMENT_ENTRY_PRESENT | \
489+
#define __SEGMENT_RW (_SEGMENT_ENTRY_PRESENT | \
476490
_SEGMENT_ENTRY_READ | \
477491
_SEGMENT_ENTRY_WRITE | \
478492
_SEGMENT_ENTRY_NOEXEC)
479-
#define SEGMENT_RWX __pgprot(_SEGMENT_ENTRY_PRESENT | \
493+
#define __SEGMENT_RWX (_SEGMENT_ENTRY_PRESENT | \
480494
_SEGMENT_ENTRY_READ | \
481495
_SEGMENT_ENTRY_WRITE)
482-
#define SEGMENT_KERNEL __pgprot(_SEGMENT_ENTRY | \
496+
#define __SEGMENT_KERNEL (_SEGMENT_ENTRY | \
483497
_SEGMENT_ENTRY_LARGE | \
484498
_SEGMENT_ENTRY_READ | \
485499
_SEGMENT_ENTRY_WRITE | \
486500
_SEGMENT_ENTRY_YOUNG | \
487501
_SEGMENT_ENTRY_DIRTY | \
488502
_SEGMENT_ENTRY_NOEXEC)
489-
#define SEGMENT_KERNEL_RO __pgprot(_SEGMENT_ENTRY | \
503+
#define __SEGMENT_KERNEL_RO (_SEGMENT_ENTRY | \
490504
_SEGMENT_ENTRY_LARGE | \
491505
_SEGMENT_ENTRY_READ | \
492506
_SEGMENT_ENTRY_YOUNG | \
493507
_SEGMENT_ENTRY_PROTECT | \
494508
_SEGMENT_ENTRY_NOEXEC)
495509

510+
extern unsigned long segment_noexec_mask;
511+
512+
#define __pgprot_segment_mask(x) __pgprot((x) & segment_noexec_mask)
513+
514+
#define SEGMENT_NONE __pgprot_segment_mask(__SEGMENT_NONE)
515+
#define SEGMENT_RO __pgprot_segment_mask(__SEGMENT_RO)
516+
#define SEGMENT_RX __pgprot_segment_mask(__SEGMENT_RX)
517+
#define SEGMENT_RW __pgprot_segment_mask(__SEGMENT_RW)
518+
#define SEGMENT_RWX __pgprot_segment_mask(__SEGMENT_RWX)
519+
#define SEGMENT_KERNEL __pgprot_segment_mask(__SEGMENT_KERNEL)
520+
#define SEGMENT_KERNEL_RO __pgprot_segment_mask(__SEGMENT_KERNEL_RO)
521+
496522
/*
497523
* Region3 entry (large page) protection definitions.
498524
*/
499525

500-
#define REGION3_KERNEL __pgprot(_REGION_ENTRY_TYPE_R3 | \
526+
#define __REGION3_KERNEL (_REGION_ENTRY_TYPE_R3 | \
501527
_REGION3_ENTRY_PRESENT | \
502-
_REGION3_ENTRY_LARGE | \
503-
_REGION3_ENTRY_READ | \
504-
_REGION3_ENTRY_WRITE | \
505-
_REGION3_ENTRY_YOUNG | \
528+
_REGION3_ENTRY_LARGE | \
529+
_REGION3_ENTRY_READ | \
530+
_REGION3_ENTRY_WRITE | \
531+
_REGION3_ENTRY_YOUNG | \
506532
_REGION3_ENTRY_DIRTY | \
507533
_REGION_ENTRY_NOEXEC)
508-
#define REGION3_KERNEL_RO __pgprot(_REGION_ENTRY_TYPE_R3 | \
509-
_REGION3_ENTRY_PRESENT | \
510-
_REGION3_ENTRY_LARGE | \
511-
_REGION3_ENTRY_READ | \
512-
_REGION3_ENTRY_YOUNG | \
513-
_REGION_ENTRY_PROTECT | \
514-
_REGION_ENTRY_NOEXEC)
534+
#define __REGION3_KERNEL_RO (_REGION_ENTRY_TYPE_R3 | \
535+
_REGION3_ENTRY_PRESENT | \
536+
_REGION3_ENTRY_LARGE | \
537+
_REGION3_ENTRY_READ | \
538+
_REGION3_ENTRY_YOUNG | \
539+
_REGION_ENTRY_PROTECT | \
540+
_REGION_ENTRY_NOEXEC)
541+
542+
extern unsigned long region_noexec_mask;
543+
544+
#define __pgprot_region_mask(x) __pgprot((x) & region_noexec_mask)
545+
546+
#define REGION3_KERNEL __pgprot_region_mask(__REGION3_KERNEL)
547+
#define REGION3_KERNEL_RO __pgprot_region_mask(__REGION3_KERNEL_RO)
515548

516549
static inline bool mm_p4d_folded(struct mm_struct *mm)
517550
{
@@ -1412,8 +1445,6 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
14121445
pte_t __pte;
14131446

14141447
__pte = __pte(physpage | pgprot_val(pgprot));
1415-
if (!MACHINE_HAS_NX)
1416-
__pte = clear_pte_bit(__pte, __pgprot(_PAGE_NOEXEC));
14171448
return pte_mkyoung(__pte);
14181449
}
14191450

@@ -1781,8 +1812,6 @@ static inline int pmdp_clear_flush_young(struct vm_area_struct *vma,
17811812
static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
17821813
pmd_t *pmdp, pmd_t entry)
17831814
{
1784-
if (!MACHINE_HAS_NX)
1785-
entry = clear_pmd_bit(entry, __pgprot(_SEGMENT_ENTRY_NOEXEC));
17861815
set_pmd(pmdp, entry);
17871816
}
17881817

arch/s390/kernel/setup.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,7 @@ void __init setup_arch(char **cmdline_p)
971971
if (test_facility(193))
972972
static_branch_enable(&cpu_has_bear);
973973

974+
setup_protection_map();
974975
/*
975976
* Create kernel page tables.
976977
*/

arch/s390/mm/init.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ pgd_t invalid_pg_dir[PTRS_PER_PGD] __section(".bss..invalid_pg_dir");
5656

5757
struct ctlreg __bootdata_preserved(s390_invalid_asce);
5858

59+
unsigned long __bootdata_preserved(page_noexec_mask);
60+
EXPORT_SYMBOL(page_noexec_mask);
61+
62+
unsigned long __bootdata_preserved(segment_noexec_mask);
63+
EXPORT_SYMBOL(segment_noexec_mask);
64+
65+
unsigned long __bootdata_preserved(region_noexec_mask);
66+
EXPORT_SYMBOL(region_noexec_mask);
67+
5968
unsigned long empty_zero_page, zero_page_mask;
6069
EXPORT_SYMBOL(empty_zero_page);
6170
EXPORT_SYMBOL(zero_page_mask);

arch/s390/mm/mmap.c

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -196,22 +196,28 @@ void arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack)
196196
}
197197
}
198198

199-
static const pgprot_t protection_map[16] = {
200-
[VM_NONE] = PAGE_NONE,
201-
[VM_READ] = PAGE_RO,
202-
[VM_WRITE] = PAGE_RO,
203-
[VM_WRITE | VM_READ] = PAGE_RO,
204-
[VM_EXEC] = PAGE_RX,
205-
[VM_EXEC | VM_READ] = PAGE_RX,
206-
[VM_EXEC | VM_WRITE] = PAGE_RX,
207-
[VM_EXEC | VM_WRITE | VM_READ] = PAGE_RX,
208-
[VM_SHARED] = PAGE_NONE,
209-
[VM_SHARED | VM_READ] = PAGE_RO,
210-
[VM_SHARED | VM_WRITE] = PAGE_RW,
211-
[VM_SHARED | VM_WRITE | VM_READ] = PAGE_RW,
212-
[VM_SHARED | VM_EXEC] = PAGE_RX,
213-
[VM_SHARED | VM_EXEC | VM_READ] = PAGE_RX,
214-
[VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_RWX,
215-
[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_RWX
216-
};
199+
static pgprot_t protection_map[16] __ro_after_init;
200+
201+
void __init setup_protection_map(void)
202+
{
203+
pgprot_t *pm = protection_map;
204+
205+
pm[VM_NONE] = PAGE_NONE;
206+
pm[VM_READ] = PAGE_RO;
207+
pm[VM_WRITE] = PAGE_RO;
208+
pm[VM_WRITE | VM_READ] = PAGE_RO;
209+
pm[VM_EXEC] = PAGE_RX;
210+
pm[VM_EXEC | VM_READ] = PAGE_RX;
211+
pm[VM_EXEC | VM_WRITE] = PAGE_RX;
212+
pm[VM_EXEC | VM_WRITE | VM_READ] = PAGE_RX;
213+
pm[VM_SHARED] = PAGE_NONE;
214+
pm[VM_SHARED | VM_READ] = PAGE_RO;
215+
pm[VM_SHARED | VM_WRITE] = PAGE_RW;
216+
pm[VM_SHARED | VM_WRITE | VM_READ] = PAGE_RW;
217+
pm[VM_SHARED | VM_EXEC] = PAGE_RX;
218+
pm[VM_SHARED | VM_EXEC | VM_READ] = PAGE_RX;
219+
pm[VM_SHARED | VM_EXEC | VM_WRITE] = PAGE_RWX;
220+
pm[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ] = PAGE_RWX;
221+
}
222+
217223
DECLARE_VM_GET_PAGE_PROT

arch/s390/mm/pageattr.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,6 @@ static int walk_pte_level(pmd_t *pmdp, unsigned long addr, unsigned long end,
109109
} else if (flags & SET_MEMORY_DEF) {
110110
new = __pte(pte_val(new) & PAGE_MASK);
111111
new = set_pte_bit(new, PAGE_KERNEL);
112-
if (!MACHINE_HAS_NX)
113-
new = clear_pte_bit(new, __pgprot(_PAGE_NOEXEC));
114112
}
115113
pgt_set((unsigned long *)ptep, pte_val(new), addr, CRDTE_DTT_PAGE);
116114
ptep++;
@@ -167,8 +165,6 @@ static void modify_pmd_page(pmd_t *pmdp, unsigned long addr,
167165
} else if (flags & SET_MEMORY_DEF) {
168166
new = __pmd(pmd_val(new) & PMD_MASK);
169167
new = set_pmd_bit(new, SEGMENT_KERNEL);
170-
if (!MACHINE_HAS_NX)
171-
new = clear_pmd_bit(new, __pgprot(_SEGMENT_ENTRY_NOEXEC));
172168
}
173169
pgt_set((unsigned long *)pmdp, pmd_val(new), addr, CRDTE_DTT_SEGMENT);
174170
}
@@ -256,8 +252,6 @@ static void modify_pud_page(pud_t *pudp, unsigned long addr,
256252
} else if (flags & SET_MEMORY_DEF) {
257253
new = __pud(pud_val(new) & PUD_MASK);
258254
new = set_pud_bit(new, REGION3_KERNEL);
259-
if (!MACHINE_HAS_NX)
260-
new = clear_pud_bit(new, __pgprot(_REGION_ENTRY_NOEXEC));
261255
}
262256
pgt_set((unsigned long *)pudp, pud_val(new), addr, CRDTE_DTT_REGION3);
263257
}

arch/s390/mm/pgtable.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,6 @@ void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr,
360360
pgste_t pgste;
361361
struct mm_struct *mm = vma->vm_mm;
362362

363-
if (!MACHINE_HAS_NX)
364-
pte = clear_pte_bit(pte, __pgprot(_PAGE_NOEXEC));
365363
if (mm_has_pgste(mm)) {
366364
pgste = pgste_get(ptep);
367365
pgste_set_key(ptep, pgste, pte, mm);

arch/s390/mm/vmem.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,6 @@ static int __ref modify_pte_table(pmd_t *pmd, unsigned long addr,
171171
pte_t *pte;
172172

173173
prot = pgprot_val(PAGE_KERNEL);
174-
if (!MACHINE_HAS_NX)
175-
prot &= ~_PAGE_NOEXEC;
176-
177174
pte = pte_offset_kernel(pmd, addr);
178175
for (; addr < end; addr += PAGE_SIZE, pte++) {
179176
if (!add) {
@@ -230,9 +227,6 @@ static int __ref modify_pmd_table(pud_t *pud, unsigned long addr,
230227
pte_t *pte;
231228

232229
prot = pgprot_val(SEGMENT_KERNEL);
233-
if (!MACHINE_HAS_NX)
234-
prot &= ~_SEGMENT_ENTRY_NOEXEC;
235-
236230
pmd = pmd_offset(pud, addr);
237231
for (; addr < end; addr = next, pmd++) {
238232
next = pmd_addr_end(addr, end);
@@ -324,8 +318,6 @@ static int modify_pud_table(p4d_t *p4d, unsigned long addr, unsigned long end,
324318
pmd_t *pmd;
325319

326320
prot = pgprot_val(REGION3_KERNEL);
327-
if (!MACHINE_HAS_NX)
328-
prot &= ~_REGION_ENTRY_NOEXEC;
329321
pud = pud_offset(p4d, addr);
330322
for (; addr < end; addr = next, pud++) {
331323
next = pud_addr_end(addr, end);

0 commit comments

Comments
 (0)