FAILED: patch "[PATCH] s390/mm: four page table levels vs. fork" failed to apply to 3.10-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 3.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
Quoted 1 line
From
3446c13b268af86391d06611327006b059b8bab1Mon Sep 17 00:00:00 2001
From: Martin Schwidefsky <[email protected]>
Date: Mon, 15 Feb 2016 14:46:49 +0100
Subject: [PATCH] s390/mm: four page table levels vs. fork
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The fork of a process with four page table levels is broken since git commit 6252d702c5311ce9 "[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.
arch/s390/include/asm/mmu_context.h +11 -5
@@ -15,17 +15,25 @@static inline int init_new_context (struct task_struct *tsk,struct mm_struct *mm) { +spin_lock_init (&mm->context.list_lock); +INIT_LIST_HEAD (&mm->context.pgtable_list); +INIT_LIST_HEAD (&mm->context.gmap_list);cpumask_clear (&mm->context.cpu_attach_mask);atomic_set (&mm->context.attach_count,0 ); mm->context.flush_mm =0 ; - mm->context.asce_bits = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS; - mm->context.asce_bits |= _ASCE_TYPE_REGION3;#ifdef CONFIG_PGSTE mm->context.alloc_pgste = page_table_allocate_pgste; mm->context.has_pgste =0 ; mm->context.use_skey =0 ;#endif - mm->context.asce_limit =STACK_TOP_MAX ; +if (mm->context.asce_limit ==0 ) { +/* context created by exec, set asce limit to 4TB */ + mm->context.asce_bits = _ASCE_TABLE_LENGTH | + _ASCE_USER_BITS | _ASCE_TYPE_REGION3; + mm->context.asce_limit =STACK_TOP_MAX ; + }else if (mm->context.asce_limit == (1UL <<31 )) { +mm_inc_nr_pmds (mm); + }crst_table_init ((unsigned long *) mm->pgd,pgd_entry_type (mm));return 0 ; }
@@ -111,8 +119,6 @@ static inline void activate_mm(struct mm_struct *prev,static inline void arch_dup_mmap (struct mm_struct *oldmm,struct mm_struct *mm) { -if (oldmm->context.asce_limit < mm->context.asce_limit) -crst_table_downgrade (mm, oldmm->context.asce_limit); }static inline void arch_exit_mmap (struct mm_struct *mm)
arch/s390/include/asm/pgalloc.h +19 -5
@@ -100,12 +100,26 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)static inline pgd_t *pgd_alloc (struct mm_struct *mm) { -spin_lock_init (&mm->context.list_lock); -INIT_LIST_HEAD (&mm->context.pgtable_list); -INIT_LIST_HEAD (&mm->context.gmap_list); -return (pgd_t *)crst_table_alloc (mm); +unsigned long *table =crst_table_alloc (mm); + +if (!table) +return NULL ; +if (mm->context.asce_limit == (1UL <<31 )) { +/* Forking a compat process with 2 page table levels */ +if (!pgtable_pmd_page_ctor (virt_to_page (table))) { +crst_table_free (mm, table); +return NULL ; + } + } +return (pgd_t *) table; +} + +static inline void pgd_free (struct mm_struct *mm,pgd_t *pgd) +{ +if (mm->context.asce_limit == (1UL <<31 )) +pgtable_pmd_page_dtor (virt_to_page (pgd)); +crst_table_free (mm, (unsigned long *) pgd); } -#define pgd_free (mm, pgd) crst_table_free(mm, (unsigned long *) pgd)static inline void pmd_populate (struct mm_struct *mm,pmd_t *pmd,pgtable_t pte)