Skip to content

Commit b6a016d

Browse files
Martin SchwidefskyDinh Nguyen
authored andcommitted
s390/mm: four page table levels vs. fork
[ Upstream commit 3446c13 ] The fork of a process with four page table levels is broken since git commit 6252d70 "[S390] dynamic page tables." All new mm contexts are created with three page table levels and an asce limit of 4TB. If the parent has four levels dup_mmap will add vmas to the new context which are outside of the asce limit. The subsequent call to copy_page_range will walk the three level page table structure of the new process with non-zero pgd and pud indexes. This leads to memory clobbers as the pgd_index *and* the pud_index is added to the mm->pgd pointer without a pgd_deref in between. The init_new_context() function is selecting the number of page table levels for a new context. The function is used by mm_init() which in turn is called by dup_mm() and mm_alloc(). These two are used by fork() and exec(). The init_new_context() function can distinguish the two cases by looking at mm->context.asce_limit, for fork() the mm struct has been copied and the number of page table levels may not change. For exec() the mm_alloc() function set the new mm structure to zero, in this case a three-level page table is created as the temporary stack space is located at STACK_TOP_MAX = 4TB. This fixes CVE-2016-2143. Reported-by: Marcin Kościelnicki <[email protected]> Reviewed-by: Heiko Carstens <[email protected]> Cc: [email protected] Signed-off-by: Martin Schwidefsky <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 59f6cb6 commit b6a016d

File tree

2 files changed

+30
-10
lines changed

2 files changed

+30
-10
lines changed

arch/s390/include/asm/mmu_context.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,25 @@
1515
static inline int init_new_context(struct task_struct *tsk,
1616
struct mm_struct *mm)
1717
{
18+
spin_lock_init(&mm->context.list_lock);
19+
INIT_LIST_HEAD(&mm->context.pgtable_list);
20+
INIT_LIST_HEAD(&mm->context.gmap_list);
1821
cpumask_clear(&mm->context.cpu_attach_mask);
1922
atomic_set(&mm->context.attach_count, 0);
2023
mm->context.flush_mm = 0;
21-
mm->context.asce_bits = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
22-
mm->context.asce_bits |= _ASCE_TYPE_REGION3;
2324
#ifdef CONFIG_PGSTE
2425
mm->context.alloc_pgste = page_table_allocate_pgste;
2526
mm->context.has_pgste = 0;
2627
mm->context.use_skey = 0;
2728
#endif
28-
mm->context.asce_limit = STACK_TOP_MAX;
29+
if (mm->context.asce_limit == 0) {
30+
/* context created by exec, set asce limit to 4TB */
31+
mm->context.asce_bits = _ASCE_TABLE_LENGTH |
32+
_ASCE_USER_BITS | _ASCE_TYPE_REGION3;
33+
mm->context.asce_limit = STACK_TOP_MAX;
34+
} else if (mm->context.asce_limit == (1UL << 31)) {
35+
mm_inc_nr_pmds(mm);
36+
}
2937
crst_table_init((unsigned long *) mm->pgd, pgd_entry_type(mm));
3038
return 0;
3139
}
@@ -111,8 +119,6 @@ static inline void activate_mm(struct mm_struct *prev,
111119
static inline void arch_dup_mmap(struct mm_struct *oldmm,
112120
struct mm_struct *mm)
113121
{
114-
if (oldmm->context.asce_limit < mm->context.asce_limit)
115-
crst_table_downgrade(mm, oldmm->context.asce_limit);
116122
}
117123

118124
static inline void arch_exit_mmap(struct mm_struct *mm)

arch/s390/include/asm/pgalloc.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,26 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
100100

101101
static inline pgd_t *pgd_alloc(struct mm_struct *mm)
102102
{
103-
spin_lock_init(&mm->context.list_lock);
104-
INIT_LIST_HEAD(&mm->context.pgtable_list);
105-
INIT_LIST_HEAD(&mm->context.gmap_list);
106-
return (pgd_t *) crst_table_alloc(mm);
103+
unsigned long *table = crst_table_alloc(mm);
104+
105+
if (!table)
106+
return NULL;
107+
if (mm->context.asce_limit == (1UL << 31)) {
108+
/* Forking a compat process with 2 page table levels */
109+
if (!pgtable_pmd_page_ctor(virt_to_page(table))) {
110+
crst_table_free(mm, table);
111+
return NULL;
112+
}
113+
}
114+
return (pgd_t *) table;
115+
}
116+
117+
static inline void pgd_free(struct mm_struct *mm, pgd_t *pgd)
118+
{
119+
if (mm->context.asce_limit == (1UL << 31))
120+
pgtable_pmd_page_dtor(virt_to_page(pgd));
121+
crst_table_free(mm, (unsigned long *) pgd);
107122
}
108-
#define pgd_free(mm, pgd) crst_table_free(mm, (unsigned long *) pgd)
109123

110124
static inline void pmd_populate(struct mm_struct *mm,
111125
pmd_t *pmd, pgtable_t pte)

0 commit comments

Comments
 (0)