From 1819ed1f06ade476b701ff764e6cd84a36aee8de Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 16 Feb 2013 11:47:27 +0100 Subject: s390/page table dumper: add support for change-recording override bit Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pgtable.h | 2 ++ arch/s390/mm/dump_pagetables.c | 25 ++++++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 97de1200c84..4a2930844d4 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -340,6 +340,8 @@ extern unsigned long MODULES_END; #define _REGION3_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INV) #define _REGION3_ENTRY_LARGE 0x400 /* RTTE-format control, large page */ +#define _REGION3_ENTRY_RO 0x200 /* page protection bit */ +#define _REGION3_ENTRY_CO 0x100 /* change-recording override */ /* Bits in the segment table entry */ #define _SEGMENT_ENTRY_ORIGIN ~0x7ffUL/* segment table origin */ diff --git a/arch/s390/mm/dump_pagetables.c b/arch/s390/mm/dump_pagetables.c index 04e4892247d..3ad65b04ac1 100644 --- a/arch/s390/mm/dump_pagetables.c +++ b/arch/s390/mm/dump_pagetables.c @@ -49,10 +49,13 @@ static void print_prot(struct seq_file *m, unsigned int pr, int level) { "ASCE", "PGD", "PUD", "PMD", "PTE" }; seq_printf(m, "%s ", level_name[level]); - if (pr & _PAGE_INVALID) + if (pr & _PAGE_INVALID) { seq_printf(m, "I\n"); - else - seq_printf(m, "%s\n", pr & _PAGE_RO ? "RO" : "RW"); + return; + } + seq_printf(m, "%s", pr & _PAGE_RO ? "RO " : "RW "); + seq_printf(m, "%s", pr & _PAGE_CO ? "CO " : " "); + seq_putc(m, '\n'); } static void note_page(struct seq_file *m, struct pg_state *st, @@ -125,6 +128,12 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, } } +#ifdef CONFIG_64BIT +#define _PMD_PROT_MASK (_SEGMENT_ENTRY_RO | _SEGMENT_ENTRY_CO) +#else +#define _PMD_PROT_MASK 0 +#endif + static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pud_t *pud, unsigned long addr) { @@ -137,7 +146,7 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, pmd = pmd_offset(pud, addr); if (!pmd_none(*pmd)) { if (pmd_large(*pmd)) { - prot = pmd_val(*pmd) & _SEGMENT_ENTRY_RO; + prot = pmd_val(*pmd) & _PMD_PROT_MASK; note_page(m, st, prot, 3); } else walk_pte_level(m, st, pmd, addr); @@ -147,6 +156,12 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, } } +#ifdef CONFIG_64BIT +#define _PUD_PROT_MASK (_REGION3_ENTRY_RO | _REGION3_ENTRY_CO) +#else +#define _PUD_PROT_MASK 0 +#endif + static void walk_pud_level(struct seq_file *m, struct pg_state *st, pgd_t *pgd, unsigned long addr) { @@ -159,7 +174,7 @@ static void walk_pud_level(struct seq_file *m, struct pg_state *st, pud = pud_offset(pgd, addr); if (!pud_none(*pud)) if (pud_large(*pud)) { - prot = pud_val(*pud) & _PAGE_RO; + prot = pud_val(*pud) & _PUD_PROT_MASK; note_page(m, st, prot, 2); } else walk_pmd_level(m, st, pud, addr); -- cgit v1.2.3 From 17ea345a474925ad0da2ad5a8791cd5443301667 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 16 Feb 2013 12:42:35 +0100 Subject: s390/mm: ignore change bit for vmemmap Add hint to the page tables that we don't care about the change bit in storage keys that belong to vmemmap pages. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/mm/vmem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index e21aaf4f5cb..ffab84db690 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c @@ -236,7 +236,8 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) if (!new_page) goto out; pmd_val(*pm_dir) = __pa(new_page) | - _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE; + _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE | + _SEGMENT_ENTRY_CO; address = (address + PMD_SIZE) & PMD_MASK; continue; } -- cgit v1.2.3 From d7b788cd06a3080ebf0b2f5f2e008d4fda2b336e Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 20 Feb 2013 13:59:26 +0100 Subject: s390/uaccess: shorten strncpy_from_user/strnlen_user Always stay within page boundaries when copying from user within strlen_user_mvcos()/strncpy_from_user_mvcos(). This allows to shorten the code a bit and may prevent unnecessary faults, since we copy quite large amounts of memory to kernel space. Also directly call the mvcos variants of copy_from_user() to avoid indirect branches. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/lib/uaccess_mvcos.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'arch') diff --git a/arch/s390/lib/uaccess_mvcos.c b/arch/s390/lib/uaccess_mvcos.c index 2443ae476e3..7f3eb3d5602 100644 --- a/arch/s390/lib/uaccess_mvcos.c +++ b/arch/s390/lib/uaccess_mvcos.c @@ -162,19 +162,19 @@ static size_t clear_user_mvcos(size_t size, void __user *to) static size_t strnlen_user_mvcos(size_t count, const char __user *src) { + size_t done, len, offset, len_str; char buf[256]; - int rc; - size_t done, len, len_str; done = 0; do { - len = min(count - done, (size_t) 256); - rc = uaccess.copy_from_user(len, src + done, buf); - if (unlikely(rc == len)) + offset = (size_t)src & ~PAGE_MASK; + len = min(256UL, PAGE_SIZE - offset); + len = min(count - done, len); + if (copy_from_user_mvcos(len, src, buf)) return 0; - len -= rc; len_str = strnlen(buf, len); done += len_str; + src += len_str; } while ((len_str == len) && (done < count)); return done + 1; } @@ -182,18 +182,18 @@ static size_t strnlen_user_mvcos(size_t count, const char __user *src) static size_t strncpy_from_user_mvcos(size_t count, const char __user *src, char *dst) { - int rc; - size_t done, len, len_str; + size_t done, len, offset, len_str; done = 0; do { - len = min(count - done, (size_t) 4096); - rc = uaccess.copy_from_user(len, src + done, dst); - if (unlikely(rc == len)) + offset = (size_t)src & ~PAGE_MASK; + len = min(count - done, PAGE_SIZE - offset); + if (copy_from_user_mvcos(len, src, dst)) return -EFAULT; - len -= rc; len_str = strnlen(dst, len); done += len_str; + src += len_str; + dst += len_str; } while ((len_str == len) && (done < count)); return done; } -- cgit v1.2.3 From f45655f6a65538237359abce55edab9cfcc6d82f Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 21 Feb 2013 13:30:42 +0100 Subject: s390/uaccess: fix strncpy_from_user/strnlen_user zero maxlen case If the maximum length specified for the to be accessed string for strncpy_from_user() and strnlen_user() is zero the following incorrect values would be returned or incorrect memory accesses would happen: strnlen_user_std() and strnlen_user_pt() incorrectly return "1" strncpy_from_user_pt() would incorrectly access "dst[maxlen - 1]" strncpy_from_user_mvcos() would incorrectly return "-EFAULT" Fix all these oddities by adding early checks. Reviewed-by: Gerald Schaefer Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/lib/uaccess_mvcos.c | 2 ++ arch/s390/lib/uaccess_pt.c | 4 ++++ arch/s390/lib/uaccess_std.c | 2 ++ 3 files changed, 8 insertions(+) (limited to 'arch') diff --git a/arch/s390/lib/uaccess_mvcos.c b/arch/s390/lib/uaccess_mvcos.c index 7f3eb3d5602..1829742bf47 100644 --- a/arch/s390/lib/uaccess_mvcos.c +++ b/arch/s390/lib/uaccess_mvcos.c @@ -184,6 +184,8 @@ static size_t strncpy_from_user_mvcos(size_t count, const char __user *src, { size_t done, len, offset, len_str; + if (unlikely(!count)) + return 0; done = 0; do { offset = (size_t)src & ~PAGE_MASK; diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index a70ee84c024..c1aaf22c326 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -172,6 +172,8 @@ static size_t strnlen_user_pt(size_t count, const char __user *src) unsigned long offset, done, len, kaddr; size_t len_str; + if (unlikely(!count)) + return 0; if (segment_eq(get_fs(), KERNEL_DS)) return strnlen((const char __kernel __force *) src, count) + 1; done = 0; @@ -202,6 +204,8 @@ static size_t strncpy_from_user_pt(size_t count, const char __user *src, { size_t n = strnlen_user_pt(count, src); + if (unlikely(!count)) + return 0; if (!n) return -EFAULT; if (n > count) diff --git a/arch/s390/lib/uaccess_std.c b/arch/s390/lib/uaccess_std.c index 6fbd0633827..79c6c7d76e0 100644 --- a/arch/s390/lib/uaccess_std.c +++ b/arch/s390/lib/uaccess_std.c @@ -188,6 +188,8 @@ size_t strnlen_user_std(size_t size, const char __user *src) register unsigned long reg0 asm("0") = 0UL; unsigned long tmp1, tmp2; + if (unlikely(!size)) + return 0; asm volatile( " la %2,0(%1)\n" " la %3,0(%0,%1)\n" -- cgit v1.2.3 From d12a2970385cfe0c1b0439f53c3ed1055d4dee3e Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 21 Feb 2013 16:57:42 +0100 Subject: s390/uaccess: remove pointless access_ok() checks access_ok() always returns 'true' on s390. Therefore all calls are quite pointless and can be removed. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/futex.h | 6 ------ arch/s390/include/asm/uaccess.h | 23 +++++------------------ arch/s390/kernel/compat_signal.c | 14 -------------- arch/s390/kernel/signal.c | 8 -------- 4 files changed, 5 insertions(+), 46 deletions(-) (limited to 'arch') diff --git a/arch/s390/include/asm/futex.h b/arch/s390/include/asm/futex.h index 96bc83ea5c9..51bcaa0fdee 100644 --- a/arch/s390/include/asm/futex.h +++ b/arch/s390/include/asm/futex.h @@ -16,9 +16,6 @@ static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) oparg = 1 << oparg; - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) - return -EFAULT; - pagefault_disable(); ret = uaccess.futex_atomic_op(op, uaddr, oparg, &oldval); pagefault_enable(); @@ -40,9 +37,6 @@ static inline int futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval) { - if (! access_ok (VERIFY_WRITE, uaddr, sizeof(u32))) - return -EFAULT; - return uaccess.futex_atomic_cmpxchg(uval, uaddr, oldval, newval); } diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h index 34268df959a..9c33ed4e666 100644 --- a/arch/s390/include/asm/uaccess.h +++ b/arch/s390/include/asm/uaccess.h @@ -252,9 +252,7 @@ static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n) { might_fault(); - if (access_ok(VERIFY_WRITE, to, n)) - n = __copy_to_user(to, from, n); - return n; + return __copy_to_user(to, from, n); } /** @@ -315,11 +313,7 @@ copy_from_user(void *to, const void __user *from, unsigned long n) copy_from_user_overflow(); return n; } - if (access_ok(VERIFY_READ, from, n)) - n = __copy_from_user(to, from, n); - else - memset(to, 0, n); - return n; + return __copy_from_user(to, from, n); } static inline unsigned long __must_check @@ -332,9 +326,7 @@ static inline unsigned long __must_check copy_in_user(void __user *to, const void __user *from, unsigned long n) { might_fault(); - if (__access_ok(from,n) && __access_ok(to,n)) - n = __copy_in_user(to, from, n); - return n; + return __copy_in_user(to, from, n); } /* @@ -343,11 +335,8 @@ copy_in_user(void __user *to, const void __user *from, unsigned long n) static inline long __must_check strncpy_from_user(char *dst, const char __user *src, long count) { - long res = -EFAULT; might_fault(); - if (access_ok(VERIFY_READ, src, 1)) - res = uaccess.strncpy_from_user(count, src, dst); - return res; + return uaccess.strncpy_from_user(count, src, dst); } static inline unsigned long @@ -387,9 +376,7 @@ static inline unsigned long __must_check clear_user(void __user *to, unsigned long n) { might_fault(); - if (access_ok(VERIFY_WRITE, to, n)) - n = uaccess.clear_user(n, to); - return n; + return uaccess.clear_user(n, to); } extern int copy_to_user_real(void __user *dest, void *src, size_t count); diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index 3e71194c190..6de049fbe62 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -53,9 +53,6 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) { int err; - if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t))) - return -EFAULT; - /* If you change siginfo_t structure, please be sure this code is fixed accordingly. It should never copy any pad contained in the structure @@ -110,9 +107,6 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) int err; u32 tmp; - if (!access_ok (VERIFY_READ, from, sizeof(compat_siginfo_t))) - return -EFAULT; - err = __get_user(to->si_signo, &from->si_signo); err |= __get_user(to->si_errno, &from->si_errno); err |= __get_user(to->si_code, &from->si_code); @@ -244,8 +238,6 @@ asmlinkage long sys32_sigreturn(void) sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15]; sigset_t set; - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) goto badframe; set_current_blocked(&set); @@ -265,8 +257,6 @@ asmlinkage long sys32_rt_sigreturn(void) rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15]; sigset_t set; - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; set_current_blocked(&set); @@ -325,8 +315,6 @@ static int setup_frame32(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(sigframe32)); - if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe32))) - goto give_sigsegv; if (frame == (void __user *) -1UL) goto give_sigsegv; @@ -391,8 +379,6 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, { int err = 0; rt_sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(rt_sigframe32)); - if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe32))) - goto give_sigsegv; if (frame == (void __user *) -1UL) goto give_sigsegv; diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 9c6e747a5e1..c45becf82e0 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -116,8 +116,6 @@ SYSCALL_DEFINE0(sigreturn) sigframe __user *frame = (sigframe __user *)regs->gprs[15]; sigset_t set; - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) goto badframe; set_current_blocked(&set); @@ -135,8 +133,6 @@ SYSCALL_DEFINE0(rt_sigreturn) rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; sigset_t set; - if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) - goto badframe; if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; set_current_blocked(&set); @@ -195,8 +191,6 @@ static int setup_frame(int sig, struct k_sigaction *ka, sigframe __user *frame; frame = get_sigframe(ka, regs, sizeof(sigframe)); - if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe))) - goto give_sigsegv; if (frame == (void __user *) -1UL) goto give_sigsegv; @@ -264,8 +258,6 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, rt_sigframe __user *frame; frame = get_sigframe(ka, regs, sizeof(rt_sigframe)); - if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe))) - goto give_sigsegv; if (frame == (void __user *) -1UL) goto give_sigsegv; -- cgit v1.2.3 From db7760ad9b43bfc1ec4baeb7011142cbe069e8f8 Mon Sep 17 00:00:00 2001 From: Syam Sidhardhan Date: Mon, 25 Feb 2013 03:45:45 +0530 Subject: s390/dis: Fix invalid array size We are using sizeof operator for an array given as function argument, which is incorrect. Signed-off-by: Syam Sidhardhan Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/dis.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index c50665fe943..3ad5e954016 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c @@ -1711,10 +1711,10 @@ int insn_to_mnemonic(unsigned char *instruction, char buf[8]) if (!insn) return -ENOENT; if (insn->name[0] == '\0') - snprintf(buf, sizeof(buf), "%s", + snprintf(buf, 8, "%s", long_insn_name[(int) insn->name[1]]); else - snprintf(buf, sizeof(buf), "%.5s", insn->name); + snprintf(buf, 8, "%.5s", insn->name); return 0; } EXPORT_SYMBOL_GPL(insn_to_mnemonic); -- cgit v1.2.3 From 225cf8d69c768f4472d2fd9f54bba2b69a588193 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 25 Feb 2013 07:24:20 +0100 Subject: s390/uaccess: fix strncpy_from_user string length check The "standard" and page table walk variants of strncpy_from_user() first check the length of the to be copied string in userspace. The string is then copied to kernel space and the length returned to the caller. However userspace can modify the string at any time while the kernel checks for the length of the string or copies the string. In result the returned length of the string is not necessarily correct. Fix this by copying in a loop which mimics the mvcos variant of strncpy_from_user(), which handles this correctly. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/lib/uaccess_pt.c | 34 +++++++++++++++++---------------- arch/s390/lib/uaccess_std.c | 46 ++++++++++++++++----------------------------- 2 files changed, 34 insertions(+), 46 deletions(-) (limited to 'arch') diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index c1aaf22c326..304e07086ab 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -202,27 +202,29 @@ fault: static size_t strncpy_from_user_pt(size_t count, const char __user *src, char *dst) { - size_t n = strnlen_user_pt(count, src); + size_t done, len, offset, len_str; if (unlikely(!count)) return 0; - if (!n) - return -EFAULT; - if (n > count) - n = count; if (segment_eq(get_fs(), KERNEL_DS)) { - memcpy(dst, (const char __kernel __force *) src, n); - if (dst[n-1] == '\0') - return n-1; - else - return n; + len = strnlen((const char __kernel __force *) src, count) + 1; + if (len > count) + len = count; + memcpy(dst, (const char __kernel __force *) src, len); + return (dst[len - 1] == '\0') ? len - 1 : len; } - if (__user_copy_pt((unsigned long) src, dst, n, 0)) - return -EFAULT; - if (dst[n-1] == '\0') - return n-1; - else - return n; + done = 0; + do { + offset = (size_t)src & ~PAGE_MASK; + len = min(count - done, PAGE_SIZE - offset); + if (__user_copy_pt((unsigned long) src, dst, len, 0)) + return -EFAULT; + len_str = strnlen(dst, len); + done += len_str; + src += len_str; + dst += len_str; + } while ((len_str == len) && (done < count)); + return done; } static size_t copy_in_user_pt(size_t n, void __user *to, diff --git a/arch/s390/lib/uaccess_std.c b/arch/s390/lib/uaccess_std.c index 79c6c7d76e0..4a75d475b06 100644 --- a/arch/s390/lib/uaccess_std.c +++ b/arch/s390/lib/uaccess_std.c @@ -206,38 +206,24 @@ size_t strnlen_user_std(size_t size, const char __user *src) return size; } -size_t strncpy_from_user_std(size_t size, const char __user *src, char *dst) +size_t strncpy_from_user_std(size_t count, const char __user *src, char *dst) { - register unsigned long reg0 asm("0") = 0UL; - unsigned long tmp1, tmp2; + size_t done, len, offset, len_str; - asm volatile( - " la %3,0(%1)\n" - " la %4,0(%0,%1)\n" - " sacf 256\n" - "0: srst %4,%3\n" - " jo 0b\n" - " sacf 0\n" - " la %0,0(%4)\n" - " jh 1f\n" /* found \0 in string ? */ - " "AHI" %4,1\n" /* include \0 in copy */ - "1:"SLR" %0,%1\n" /* %0 = return length (without \0) */ - " "SLR" %4,%1\n" /* %4 = copy length (including \0) */ - "2: mvcp 0(%4,%2),0(%1),%5\n" - " jz 9f\n" - "3:"AHI" %4,-256\n" - " la %1,256(%1)\n" - " la %2,256(%2)\n" - "4: mvcp 0(%4,%2),0(%1),%5\n" - " jnz 3b\n" - " j 9f\n" - "7: sacf 0\n" - "8:"LHI" %0,%6\n" - "9:\n" - EX_TABLE(0b,7b) EX_TABLE(2b,8b) EX_TABLE(4b,8b) - : "+a" (size), "+a" (src), "+d" (dst), "=a" (tmp1), "=a" (tmp2) - : "d" (reg0), "K" (-EFAULT) : "cc", "memory"); - return size; + if (unlikely(!count)) + return 0; + done = 0; + do { + offset = (size_t)src & ~PAGE_MASK; + len = min(count - done, PAGE_SIZE - offset); + if (copy_from_user_std(len, src, dst)) + return -EFAULT; + len_str = strnlen(dst, len); + done += len_str; + src += len_str; + dst += len_str; + } while ((len_str == len) && (done < count)); + return done; } #define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg) \ -- cgit v1.2.3 From 066c4373599211ab0e1425586b8df6f1e932d97e Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Mon, 25 Feb 2013 09:10:23 +0100 Subject: s390/uaccess: fix kernel ds access for page table walk When the kernel resides in home space and the mvcos instruction is not available uaccesses for kernel ds happen via simple strnlen() or memcpy() calls. This however can break badly, since uaccesses in kernel space may fail as well, especially if CONFIG_DEBUG_PAGEALLOC is turned on. To fix this implement strnlen_kernel() and copy_in_kernel() functions which can only be used by the page table uaccess functions. These two functions detect invalid memory accesses and return the correct length of processed data.. Both functions are more or less a copy of the std variants without sacf calls. Fixes ipl crashes on 31 bit machines as well on 64 bit machines without mvcos. Caused by changing the default address space of the kernel being home space. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/lib/uaccess_pt.c | 105 +++++++++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 28 deletions(-) (limited to 'arch') diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index 304e07086ab..dff631d34b4 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -14,6 +14,63 @@ #include #include "uaccess.h" +#ifndef CONFIG_64BIT +#define AHI "ahi" +#define SLR "slr" +#else +#define AHI "aghi" +#define SLR "slgr" +#endif + +static size_t strnlen_kernel(size_t count, const char __user *src) +{ + register unsigned long reg0 asm("0") = 0UL; + unsigned long tmp1, tmp2; + + asm volatile( + " la %2,0(%1)\n" + " la %3,0(%0,%1)\n" + " "SLR" %0,%0\n" + "0: srst %3,%2\n" + " jo 0b\n" + " la %0,1(%3)\n" /* strnlen_kernel results includes \0 */ + " "SLR" %0,%1\n" + "1:\n" + EX_TABLE(0b,1b) + : "+a" (count), "+a" (src), "=a" (tmp1), "=a" (tmp2) + : "d" (reg0) : "cc", "memory"); + return count; +} + +static size_t copy_in_kernel(size_t count, void __user *to, + const void __user *from) +{ + unsigned long tmp1; + + asm volatile( + " "AHI" %0,-1\n" + " jo 5f\n" + " bras %3,3f\n" + "0:"AHI" %0,257\n" + "1: mvc 0(1,%1),0(%2)\n" + " la %1,1(%1)\n" + " la %2,1(%2)\n" + " "AHI" %0,-1\n" + " jnz 1b\n" + " j 5f\n" + "2: mvc 0(256,%1),0(%2)\n" + " la %1,256(%1)\n" + " la %2,256(%2)\n" + "3:"AHI" %0,-256\n" + " jnm 2b\n" + "4: ex %0,1b-0b(%3)\n" + "5:"SLR" %0,%0\n" + "6:\n" + EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b) + : "+a" (count), "+a" (to), "+a" (from), "=a" (tmp1) + : : "cc", "memory"); + return count; +} /* * Returns kernel address for user virtual address. If the returned address is @@ -123,10 +180,8 @@ size_t copy_from_user_pt(size_t n, const void __user *from, void *to) { size_t rc; - if (segment_eq(get_fs(), KERNEL_DS)) { - memcpy(to, (void __kernel __force *) from, n); - return 0; - } + if (segment_eq(get_fs(), KERNEL_DS)) + return copy_in_kernel(n, (void __user *) to, from); rc = __user_copy_pt((unsigned long) from, to, n, 0); if (unlikely(rc)) memset(to + n - rc, 0, rc); @@ -135,30 +190,28 @@ size_t copy_from_user_pt(size_t n, const void __user *from, void *to) size_t copy_to_user_pt(size_t n, void __user *to, const void *from) { - if (segment_eq(get_fs(), KERNEL_DS)) { - memcpy((void __kernel __force *) to, from, n); - return 0; - } + if (segment_eq(get_fs(), KERNEL_DS)) + return copy_in_kernel(n, to, (void __user *) from); return __user_copy_pt((unsigned long) to, (void *) from, n, 1); } static size_t clear_user_pt(size_t n, void __user *to) { + void *zpage = &empty_zero_page; long done, size, ret; - if (segment_eq(get_fs(), KERNEL_DS)) { - memset((void __kernel __force *) to, 0, n); - return 0; - } done = 0; do { if (n - done > PAGE_SIZE) size = PAGE_SIZE; else size = n - done; - ret = __user_copy_pt((unsigned long) to + done, - &empty_zero_page, size, 1); + if (segment_eq(get_fs(), KERNEL_DS)) + ret = copy_in_kernel(n, to, (void __user *) zpage); + else + ret = __user_copy_pt((unsigned long) to, zpage, size, 1); done += size; + to += size; if (ret) return ret + n - done; } while (done < n); @@ -175,7 +228,7 @@ static size_t strnlen_user_pt(size_t count, const char __user *src) if (unlikely(!count)) return 0; if (segment_eq(get_fs(), KERNEL_DS)) - return strnlen((const char __kernel __force *) src, count) + 1; + return strnlen_kernel(count, src); done = 0; retry: spin_lock(&mm->page_table_lock); @@ -206,19 +259,17 @@ static size_t strncpy_from_user_pt(size_t count, const char __user *src, if (unlikely(!count)) return 0; - if (segment_eq(get_fs(), KERNEL_DS)) { - len = strnlen((const char __kernel __force *) src, count) + 1; - if (len > count) - len = count; - memcpy(dst, (const char __kernel __force *) src, len); - return (dst[len - 1] == '\0') ? len - 1 : len; - } done = 0; do { offset = (size_t)src & ~PAGE_MASK; len = min(count - done, PAGE_SIZE - offset); - if (__user_copy_pt((unsigned long) src, dst, len, 0)) - return -EFAULT; + if (segment_eq(get_fs(), KERNEL_DS)) { + if (copy_in_kernel(len, (void __user *) dst, src)) + return -EFAULT; + } else { + if (__user_copy_pt((unsigned long) src, dst, len, 0)) + return -EFAULT; + } len_str = strnlen(dst, len); done += len_str; src += len_str; @@ -237,10 +288,8 @@ static size_t copy_in_user_pt(size_t n, void __user *to, unsigned long kaddr_to, kaddr_from; int write_user; - if (segment_eq(get_fs(), KERNEL_DS)) { - memcpy((void __force *) to, (void __force *) from, n); - return 0; - } + if (segment_eq(get_fs(), KERNEL_DS)) + return copy_in_kernel(n, to, from); done = 0; retry: spin_lock(&mm->page_table_lock); -- cgit v1.2.3 From 72a6b43e4bbb818ac1875a3a572cb2aa8c9af820 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 27 Feb 2013 16:28:20 +0100 Subject: s390/module: fix compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Get rid of this one (false positive): arch/s390/kernel/module.c: In function ‘apply_relocate_add’: arch/s390/kernel/module.c:404:5: warning: ‘rc’ may be used uninitialized in this function [-Wmaybe-uninitialized] arch/s390/kernel/module.c:225:6: note: ‘rc’ was declared here Play safe and preinitialize rc with an error value, so we see an error if new users indeed don't initialize it. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c index f750bd7bd2c..7845e15a17d 100644 --- a/arch/s390/kernel/module.c +++ b/arch/s390/kernel/module.c @@ -222,7 +222,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, struct mod_arch_syminfo *info; Elf_Addr loc, val; int r_type, r_sym; - int rc; + int rc = -ENOEXEC; /* This is where to make the change */ loc = base + rela->r_offset; -- cgit v1.2.3