From ce0c12b633846a47e103842149a5bac2e5d261ec Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 1 Mar 2016 12:58:06 +0100 Subject: s390/dasd: fix diag 0x250 inline assembly git commit 1ec2772e0c3c ("s390/diag: add a statistic for diagnose calls") added function calls to gather diagnose statistics. In case of the dasd diag driver the function call was added between a register asm statement which initialized register r2 and the inline assembly itself. The function call clobbers the contents of register r2 and therefore the diag 0x250 call behaves in a more or less random way. Fix this by extracting the function call into a separate function like we do everywhere else. Fixes: 1ec2772e0c3c ("s390/diag: add a statistic for diagnose calls") Cc: # 4.4+ Reported-and-tested-by: Stefan Haberland Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_diag.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index cb61f300f8b5..277b5c8c825c 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -67,7 +67,7 @@ static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */ * and function code cmd. * In case of an exception return 3. Otherwise return result of bitwise OR of * resulting condition code and DIAG return code. */ -static inline int dia250(void *iob, int cmd) +static inline int __dia250(void *iob, int cmd) { register unsigned long reg2 asm ("2") = (unsigned long) iob; typedef union { @@ -77,7 +77,6 @@ static inline int dia250(void *iob, int cmd) int rc; rc = 3; - diag_stat_inc(DIAG_STAT_X250); asm volatile( " diag 2,%2,0x250\n" "0: ipm %0\n" @@ -91,6 +90,12 @@ static inline int dia250(void *iob, int cmd) return rc; } +static inline int dia250(void *iob, int cmd) +{ + diag_stat_inc(DIAG_STAT_X250); + return __dia250(iob, cmd); +} + /* Initialize block I/O to DIAG device using the specified blocksize and * block offset. On success, return zero and set end_block to contain the * number of blocks on the device minus the specified offset. Return non-zero -- cgit v1.2.3-70-g09d2 From 7a76aa95f6f6682db5629449d763251d1c9f8c4e Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 29 Feb 2016 15:59:18 +0100 Subject: s390/cpumf: Fix lpp detection we have to check bit 40 of the facility list before issuing LPP and not bit 48. Otherwise a guest running on a system with "The decimal-floating-point zoned-conversion facility" and without the "The set-program-parameters facility" might crash on an lpp instruction. Signed-off-by: Christian Borntraeger Cc: stable@vger.kernel.org # v4.4+ Fixes: e22cf8ca6f75 ("s390/cpumf: rework program parameter setting to detect guest samples") Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/head64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index c5febe84eba6..03c2b469c472 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -16,7 +16,7 @@ __HEAD ENTRY(startup_continue) - tm __LC_STFLE_FAC_LIST+6,0x80 # LPP available ? + tm __LC_STFLE_FAC_LIST+5,0x80 # LPP available ? jz 0f xc __LC_LPP+1(7,0),__LC_LPP+1 # clear lpp and current_pid mvi __LC_LPP,0x80 # and set LPP_MAGIC -- cgit v1.2.3-70-g09d2 From 3446c13b268af86391d06611327006b059b8bab1 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Mon, 15 Feb 2016 14:46:49 +0100 Subject: 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. Reported-by: Marcin Koƛcielnicki Reviewed-by: Heiko Carstens Cc: stable@vger.kernel.org Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/mmu_context.h | 16 +++++++++++----- arch/s390/include/asm/pgalloc.h | 24 +++++++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h index fb1b93ea3e3f..e485817f7b1a 100644 --- a/arch/s390/include/asm/mmu_context.h +++ b/arch/s390/include/asm/mmu_context.h @@ -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) diff --git a/arch/s390/include/asm/pgalloc.h b/arch/s390/include/asm/pgalloc.h index 7b7858f158b4..d7cc79fb6191 100644 --- a/arch/s390/include/asm/pgalloc.h +++ b/arch/s390/include/asm/pgalloc.h @@ -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) -- cgit v1.2.3-70-g09d2