lkml.org 
[lkml]   [2016]   [Apr]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 3.16 185/217] s390/mm: four page table levels vs. fork
    3.16.35-rc1 review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Martin Schwidefsky <schwidefsky@de.ibm.com>

    commit 3446c13b268af86391d06611327006b059b8bab1 upstream.

    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.

    Reported-by: Marcin Kościelnicki <koriakin@0x04.net>
    Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
    Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
    [bwh: Backported to 3.16:
    - 31-bit s390 is still supported so keep the #ifdef CONFIG_64BIT conditions
    - PMDs are not accounted so don't call mm_inc_nr_pmds()
    - Adjust context]
    Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
    ---
    --- a/arch/s390/include/asm/mmu_context.h
    +++ b/arch/s390/include/asm/mmu_context.h
    @@ -15,16 +15,22 @@
    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;
    -#ifdef CONFIG_64BIT
    - mm->context.asce_bits |= _ASCE_TYPE_REGION3;
    -#endif
    mm->context.has_pgste = 0;
    mm->context.use_skey = 0;
    - 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;
    +#ifdef CONFIG_64BIT
    + mm->context.asce_bits |= _ASCE_TYPE_REGION3;
    +#endif
    + mm->context.asce_limit = STACK_TOP_MAX;
    + }
    crst_table_init((unsigned long *) mm->pgd, pgd_entry_type(mm));
    return 0;
    }
    @@ -110,10 +116,6 @@ static inline void activate_mm(struct mm
    static inline void arch_dup_mmap(struct mm_struct *oldmm,
    struct mm_struct *mm)
    {
    -#ifdef CONFIG_64BIT
    - if (oldmm->context.asce_limit < mm->context.asce_limit)
    - crst_table_downgrade(mm, oldmm->context.asce_limit);
    -#endif
    }

    static inline void arch_exit_mmap(struct mm_struct *mm)
    --- a/arch/s390/include/asm/pgalloc.h
    +++ b/arch/s390/include/asm/pgalloc.h
    @@ -124,12 +124,26 @@ static inline void pud_populate(struct m

    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)
    \
     
     \ /
      Last update: 2016-04-27 02:41    [W:3.052 / U:0.008 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site