diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-04 00:58:43 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-03-04 00:58:43 +0400 |
commit | 530ede14cf63ef4a207b0003e14af2f750484180 (patch) | |
tree | 2849be2c8fd6d2d6a8a71447aebe5d7400317c8d /arch/s390/lib/uaccess_pt.c | |
parent | 6977c6fc77b864dc1851e100c8528c2f3ed5bd9b (diff) | |
parent | 72a6b43e4bbb818ac1875a3a572cb2aa8c9af820 (diff) | |
download | linux-530ede14cf63ef4a207b0003e14af2f750484180.tar.xz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull second set of s390 patches from Martin Schwidefsky:
"The main part of this merge are Heikos uaccess patches. Together with
commit 09884964335e ("mm: do not grow the stack vma just because of an
overrun on preceding vma") the user string access is hopefully fixed
for good.
In addition some bug fixes and two cleanup patches."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
s390/module: fix compile warning
qdio: remove unused parameters
s390/uaccess: fix kernel ds access for page table walk
s390/uaccess: fix strncpy_from_user string length check
input: disable i8042 PC Keyboard controller for s390
s390/dis: Fix invalid array size
s390/uaccess: remove pointless access_ok() checks
s390/uaccess: fix strncpy_from_user/strnlen_user zero maxlen case
s390/uaccess: shorten strncpy_from_user/strnlen_user
s390/dasd: fix unresponsive device after all channel paths were lost
s390/mm: ignore change bit for vmemmap
s390/page table dumper: add support for change-recording override bit
Diffstat (limited to 'arch/s390/lib/uaccess_pt.c')
-rw-r--r-- | arch/s390/lib/uaccess_pt.c | 129 |
1 files changed, 92 insertions, 37 deletions
diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c index a70ee84c0241..dff631d34b45 100644 --- a/arch/s390/lib/uaccess_pt.c +++ b/arch/s390/lib/uaccess_pt.c @@ -14,6 +14,63 @@ #include <asm/futex.h> #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); @@ -172,8 +225,10 @@ 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; + return strnlen_kernel(count, src); done = 0; retry: spin_lock(&mm->page_table_lock); @@ -200,25 +255,27 @@ 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 (!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; - } - if (__user_copy_pt((unsigned long) src, dst, n, 0)) - return -EFAULT; - if (dst[n-1] == '\0') - return n-1; - else - return n; + if (unlikely(!count)) + return 0; + done = 0; + do { + offset = (size_t)src & ~PAGE_MASK; + len = min(count - done, PAGE_SIZE - offset); + 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; + dst += len_str; + } while ((len_str == len) && (done < count)); + return done; } static size_t copy_in_user_pt(size_t n, void __user *to, @@ -231,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); |