From 29af0ebaa24d3078d7fd9747a49a763fe7f9ea3c Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Tue, 22 May 2012 16:39:00 +0200 Subject: sparc32: use the common implementation of alloc_thread_info_node() With sun4c removed we can fall-back to the common implementation. Signed-off-by: Sam Ravnborg Cc: Thomas Gleixner Acked-by: Thomas Gleixner Signed-off-by: David S. Miller --- arch/sparc/Kconfig | 1 - arch/sparc/include/asm/thread_info_32.h | 11 ++--------- arch/sparc/mm/srmmu.c | 27 --------------------------- 3 files changed, 2 insertions(+), 37 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 1ea3fd95475..051af37f2b4 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -37,7 +37,6 @@ config SPARC32 def_bool !64BIT select GENERIC_ATOMIC64 select CLZ_TAB - select ARCH_THREAD_INFO_ALLOCATOR config SPARC64 def_bool 64BIT diff --git a/arch/sparc/include/asm/thread_info_32.h b/arch/sparc/include/asm/thread_info_32.h index 21a38946541..5af66493245 100644 --- a/arch/sparc/include/asm/thread_info_32.h +++ b/arch/sparc/include/asm/thread_info_32.h @@ -77,18 +77,11 @@ register struct thread_info *current_thread_info_reg asm("g6"); /* * thread information allocation */ -#define THREAD_INFO_ORDER 1 - -struct thread_info * alloc_thread_info_node(struct task_struct *tsk, int node); -void free_thread_info(struct thread_info *); +#define THREAD_SIZE_ORDER 1 #endif /* __ASSEMBLY__ */ -/* - * Size of kernel stack for each process. - * Observe the order of get_free_pages() in alloc_thread_info_node(). - * The sun4 has 8K stack too, because it's short on memory, and 16K is a waste. - */ +/* Size of kernel stack for each process */ #define THREAD_SIZE (2 * PAGE_SIZE) /* diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 8e97e0305b0..256db6b22c5 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -467,33 +467,6 @@ void srmmu_unmapiorange(unsigned long virt_addr, unsigned int len) flush_tlb_all(); } -/* - * On the SRMMU we do not have the problems with limited tlb entries - * for mapping kernel pages, so we just take things from the free page - * pool. As a side effect we are putting a little too much pressure - * on the gfp() subsystem. This setup also makes the logic of the - * iommu mapping code a lot easier as we can transparently handle - * mappings on the kernel stack without any special code. - */ -struct thread_info *alloc_thread_info_node(struct task_struct *tsk, int node) -{ - struct thread_info *ret; - - ret = (struct thread_info *)__get_free_pages(GFP_KERNEL, - THREAD_INFO_ORDER); -#ifdef CONFIG_DEBUG_STACK_USAGE - if (ret) - memset(ret, 0, PAGE_SIZE << THREAD_INFO_ORDER); -#endif /* DEBUG_STACK_USAGE */ - - return ret; -} - -void free_thread_info(struct thread_info *ti) -{ - free_pages((unsigned long)ti, THREAD_INFO_ORDER); -} - /* tsunami.S */ extern void tsunami_flush_cache_all(void); extern void tsunami_flush_cache_mm(struct mm_struct *mm); -- cgit v1.2.3-70-g09d2 From ff06dffbc8abfc60d6a0332f058f1d1bb01abb31 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 22 May 2012 17:53:19 -0700 Subject: sparc: Add full proper error handling to strncpy_from_user(). Linus removed the end-of-address-space hackery from fs/namei.c:do_getname() so we really have to validate these edge conditions and cannot cheat any more (as x86 used to as well). Move to a common C implementation like x86 did. And if both src and dst are sufficiently aligned we'll do word at a time copies and checks as well. Signed-off-by: David S. Miller --- arch/sparc/include/asm/uaccess.h | 3 + arch/sparc/include/asm/uaccess_32.h | 10 --- arch/sparc/include/asm/uaccess_64.h | 4 - arch/sparc/lib/Makefile | 2 +- arch/sparc/lib/ksyms.c | 3 - arch/sparc/lib/strncpy_from_user_32.S | 47 ------------ arch/sparc/lib/strncpy_from_user_64.S | 133 ---------------------------------- arch/sparc/lib/usercopy.c | 132 +++++++++++++++++++++++++++++++++ 8 files changed, 136 insertions(+), 198 deletions(-) delete mode 100644 arch/sparc/lib/strncpy_from_user_32.S delete mode 100644 arch/sparc/lib/strncpy_from_user_64.S (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/uaccess.h b/arch/sparc/include/asm/uaccess.h index e88fbe5c045..42a28cfd941 100644 --- a/arch/sparc/include/asm/uaccess.h +++ b/arch/sparc/include/asm/uaccess.h @@ -5,4 +5,7 @@ #else #include #endif + +extern long strncpy_from_user(char *dest, const char __user *src, long count); + #endif diff --git a/arch/sparc/include/asm/uaccess_32.h b/arch/sparc/include/asm/uaccess_32.h index d50c310f5d3..59586b57ef1 100644 --- a/arch/sparc/include/asm/uaccess_32.h +++ b/arch/sparc/include/asm/uaccess_32.h @@ -304,16 +304,6 @@ static inline unsigned long clear_user(void __user *addr, unsigned long n) return n; } -extern long __strncpy_from_user(char *dest, const char __user *src, long count); - -static inline long strncpy_from_user(char *dest, const char __user *src, long count) -{ - if (__access_ok((unsigned long) src, count)) - return __strncpy_from_user(dest, src, count); - else - return -EFAULT; -} - extern long __strlen_user(const char __user *); extern long __strnlen_user(const char __user *, long len); diff --git a/arch/sparc/include/asm/uaccess_64.h b/arch/sparc/include/asm/uaccess_64.h index a1091afb883..dcdfb89cbf3 100644 --- a/arch/sparc/include/asm/uaccess_64.h +++ b/arch/sparc/include/asm/uaccess_64.h @@ -257,10 +257,6 @@ extern unsigned long __must_check __clear_user(void __user *, unsigned long); #define clear_user __clear_user -extern long __must_check __strncpy_from_user(char *dest, const char __user *src, long count); - -#define strncpy_from_user __strncpy_from_user - extern long __strlen_user(const char __user *); extern long __strnlen_user(const char __user *, long len); diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index 389628f50a1..943d98dc4cd 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -10,7 +10,7 @@ lib-y += strlen.o lib-y += checksum_$(BITS).o lib-$(CONFIG_SPARC32) += blockops.o lib-y += memscan_$(BITS).o memcmp.o strncmp_$(BITS).o -lib-y += strncpy_from_user_$(BITS).o strlen_user_$(BITS).o +lib-y += strlen_user_$(BITS).o lib-$(CONFIG_SPARC32) += divdi3.o udivdi3.o lib-$(CONFIG_SPARC32) += copy_user.o locks.o lib-$(CONFIG_SPARC64) += atomic_64.o diff --git a/arch/sparc/lib/ksyms.c b/arch/sparc/lib/ksyms.c index 2dc30875c8b..6b278abdb63 100644 --- a/arch/sparc/lib/ksyms.c +++ b/arch/sparc/lib/ksyms.c @@ -33,9 +33,6 @@ EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(__bzero); -/* Moving data to/from/in userspace. */ -EXPORT_SYMBOL(__strncpy_from_user); - /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial); diff --git a/arch/sparc/lib/strncpy_from_user_32.S b/arch/sparc/lib/strncpy_from_user_32.S deleted file mode 100644 index db0ed2964bd..00000000000 --- a/arch/sparc/lib/strncpy_from_user_32.S +++ /dev/null @@ -1,47 +0,0 @@ -/* strncpy_from_user.S: Sparc strncpy from userspace. - * - * Copyright(C) 1996 David S. Miller - */ - -#include -#include -#include - - .text - - /* Must return: - * - * -EFAULT for an exception - * count if we hit the buffer limit - * bytes copied if we hit a null byte - */ - -ENTRY(__strncpy_from_user) - /* %o0=dest, %o1=src, %o2=count */ - mov %o2, %o3 -1: - subcc %o2, 1, %o2 - bneg 2f - nop -10: - ldub [%o1], %o4 - add %o0, 1, %o0 - cmp %o4, 0 - add %o1, 1, %o1 - bne 1b - stb %o4, [%o0 - 1] -2: - add %o2, 1, %o0 - retl - sub %o3, %o0, %o0 -ENDPROC(__strncpy_from_user) - - .section .fixup,#alloc,#execinstr - .align 4 -4: - retl - mov -EFAULT, %o0 - - .section __ex_table,#alloc - .align 4 - .word 10b, 4b diff --git a/arch/sparc/lib/strncpy_from_user_64.S b/arch/sparc/lib/strncpy_from_user_64.S deleted file mode 100644 index d1246b71307..00000000000 --- a/arch/sparc/lib/strncpy_from_user_64.S +++ /dev/null @@ -1,133 +0,0 @@ -/* - * strncpy_from_user.S: Sparc64 strncpy from userspace. - * - * Copyright (C) 1997, 1999 Jakub Jelinek (jj@ultra.linux.cz) - */ - -#include -#include -#include - - .data - .align 8 -0: .xword 0x0101010101010101 - - .text - - /* Must return: - * - * -EFAULT for an exception - * count if we hit the buffer limit - * bytes copied if we hit a null byte - * (without the null byte) - * - * This implementation assumes: - * %o1 is 8 aligned => !(%o2 & 7) - * %o0 is 8 aligned (if not, it will be slooooow, but will work) - * - * This is optimized for the common case: - * in my stats, 90% of src are 8 aligned (even on sparc32) - * and average length is 18 or so. - */ - -ENTRY(__strncpy_from_user) - /* %o0=dest, %o1=src, %o2=count */ - andcc %o1, 7, %g0 ! IEU1 Group - bne,pn %icc, 30f ! CTI - add %o0, %o2, %g3 ! IEU0 -60: ldxa [%o1] %asi, %g1 ! Load Group - brlez,pn %o2, 10f ! CTI - mov %o0, %o3 ! IEU0 -50: sethi %hi(0b), %o4 ! IEU0 Group - ldx [%o4 + %lo(0b)], %o4 ! Load - sllx %o4, 7, %o5 ! IEU1 Group -1: sub %g1, %o4, %g2 ! IEU0 Group - stx %g1, [%o0] ! Store - add %o0, 8, %o0 ! IEU1 - andcc %g2, %o5, %g0 ! IEU1 Group - bne,pn %xcc, 5f ! CTI - add %o1, 8, %o1 ! IEU0 - cmp %o0, %g3 ! IEU1 Group - bl,a,pt %xcc, 1b ! CTI -61: ldxa [%o1] %asi, %g1 ! Load -10: retl ! CTI Group - mov %o2, %o0 ! IEU0 -5: srlx %g2, 32, %g7 ! IEU0 Group - sethi %hi(0xff00), %o4 ! IEU1 - andcc %g7, %o5, %g0 ! IEU1 Group - be,pn %icc, 2f ! CTI - or %o4, %lo(0xff00), %o4 ! IEU0 - srlx %g1, 48, %g7 ! IEU0 Group - andcc %g7, %o4, %g0 ! IEU1 Group - be,pn %icc, 50f ! CTI - andcc %g7, 0xff, %g0 ! IEU1 Group - be,pn %icc, 51f ! CTI - srlx %g1, 32, %g7 ! IEU0 - andcc %g7, %o4, %g0 ! IEU1 Group - be,pn %icc, 52f ! CTI - andcc %g7, 0xff, %g0 ! IEU1 Group - be,pn %icc, 53f ! CTI -2: andcc %g2, %o5, %g0 ! IEU1 Group - be,pn %icc, 2f ! CTI - srl %g1, 16, %g7 ! IEU0 - andcc %g7, %o4, %g0 ! IEU1 Group - be,pn %icc, 54f ! CTI - andcc %g7, 0xff, %g0 ! IEU1 Group - be,pn %icc, 55f ! CTI - andcc %g1, %o4, %g0 ! IEU1 Group - be,pn %icc, 56f ! CTI - andcc %g1, 0xff, %g0 ! IEU1 Group - be,a,pn %icc, 57f ! CTI - sub %o0, %o3, %o0 ! IEU0 -2: cmp %o0, %g3 ! IEU1 Group - bl,a,pt %xcc, 50b ! CTI -62: ldxa [%o1] %asi, %g1 ! Load - retl ! CTI Group - mov %o2, %o0 ! IEU0 -50: sub %o0, %o3, %o0 - retl - sub %o0, 8, %o0 -51: sub %o0, %o3, %o0 - retl - sub %o0, 7, %o0 -52: sub %o0, %o3, %o0 - retl - sub %o0, 6, %o0 -53: sub %o0, %o3, %o0 - retl - sub %o0, 5, %o0 -54: sub %o0, %o3, %o0 - retl - sub %o0, 4, %o0 -55: sub %o0, %o3, %o0 - retl - sub %o0, 3, %o0 -56: sub %o0, %o3, %o0 - retl - sub %o0, 2, %o0 -57: retl - sub %o0, 1, %o0 -30: brlez,pn %o2, 3f - sub %g0, %o2, %o3 - add %o0, %o2, %o0 -63: lduba [%o1] %asi, %o4 -1: add %o1, 1, %o1 - brz,pn %o4, 2f - stb %o4, [%o0 + %o3] - addcc %o3, 1, %o3 - bne,pt %xcc, 1b -64: lduba [%o1] %asi, %o4 -3: retl - mov %o2, %o0 -2: retl - add %o2, %o3, %o0 -ENDPROC(__strncpy_from_user) - - .section __ex_table,"a" - .align 4 - .word 60b, __retl_efault - .word 61b, __retl_efault - .word 62b, __retl_efault - .word 63b, __retl_efault - .word 64b, __retl_efault - .previous diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index 14b363fec8a..851cb75ce18 100644 --- a/arch/sparc/lib/usercopy.c +++ b/arch/sparc/lib/usercopy.c @@ -1,4 +1,6 @@ #include +#include +#include #include void copy_from_user_overflow(void) @@ -6,3 +8,133 @@ void copy_from_user_overflow(void) WARN(1, "Buffer overflow detected!\n"); } EXPORT_SYMBOL(copy_from_user_overflow); + +#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) + +/* Return the high bit set in the first byte that is a zero */ +static inline unsigned long has_zero(unsigned long a) +{ + return ((a - REPEAT_BYTE(0x01)) & ~a) & REPEAT_BYTE(0x80); +} + +static inline long find_zero(unsigned long c) +{ +#ifdef CONFIG_64BIT + if (!(c & 0xff00000000000000UL)) + return 0; + if (!(c & 0x00ff000000000000UL)) + return 1; + if (!(c & 0x0000ff0000000000UL)) + return 2; + if (!(c & 0x000000ff00000000UL)) + return 3; +#define __OFF 4 +#else +#define __OFF 0 +#endif + if (!(c & 0xff000000)) + return __OFF + 0; + if (!(c & 0x00ff0000)) + return __OFF + 1; + if (!(c & 0x0000ff00)) + return __OFF + 2; + return __OFF + 3; +#undef __OFF +} + +/* + * Do a strncpy, return length of string without final '\0'. + * 'count' is the user-supplied count (return 'count' if we + * hit it), 'max' is the address space maximum (and we return + * -EFAULT if we hit it). + */ +static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) +{ + long res = 0; + + /* + * Truncate 'max' to the user-specified limit, so that + * we only have one limit we need to check in the loop + */ + if (max > count) + max = count; + + if (((long) dst | (long) src) & (sizeof(long) - 1)) + goto byte_at_a_time; + + while (max >= sizeof(unsigned long)) { + unsigned long c; + + /* Fall back to byte-at-a-time if we get a page fault */ + if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) + break; + *(unsigned long *)(dst+res) = c; + if (has_zero(c)) + return res + find_zero(c); + res += sizeof(unsigned long); + max -= sizeof(unsigned long); + } + +byte_at_a_time: + while (max) { + char c; + + if (unlikely(__get_user(c,src+res))) + return -EFAULT; + dst[res] = c; + if (!c) + return res; + res++; + max--; + } + + /* + * Uhhuh. We hit 'max'. But was that the user-specified maximum + * too? If so, that's ok - we got as much as the user asked for. + */ + if (res >= count) + return res; + + /* + * Nope: we hit the address space limit, and we still had more + * characters the caller would have wanted. That's an EFAULT. + */ + return -EFAULT; +} + +/** + * strncpy_from_user: - Copy a NUL terminated string from userspace. + * @dst: Destination address, in kernel space. This buffer must be at + * least @count bytes long. + * @src: Source address, in user space. + * @count: Maximum number of bytes to copy, including the trailing NUL. + * + * Copies a NUL-terminated string from userspace to kernel space. + * + * On success, returns the length of the string (not including the trailing + * NUL). + * + * If access to userspace fails, returns -EFAULT (some data may have been + * copied). + * + * If @count is smaller than the length of the string, copies @count bytes + * and returns @count. + */ +long strncpy_from_user(char *dst, const char __user *src, long count) +{ + unsigned long max_addr, src_addr; + + if (unlikely(count <= 0)) + return 0; + + max_addr = ~0UL; + if (likely(segment_eq(get_fs(), USER_DS))) + max_addr = STACK_TOP; + src_addr = (unsigned long)src; + if (likely(src_addr < max_addr)) { + unsigned long max = max_addr - src_addr; + return do_strncpy_from_user(dst, src, count, max); + } + return -EFAULT; +} +EXPORT_SYMBOL(strncpy_from_user); -- cgit v1.2.3-70-g09d2 From 4efcac3a244de86593a82ca4ed945e839eb4c5af Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 23 May 2012 19:20:20 -0700 Subject: sparc: Optimize strncpy_from_user() zero byte search. Compute a mask that will only have 0x80 in the bytes which had a zero in them. The formula is: ~(((x & 0x7f7f7f7f) + 0x7f7f7f7f) | x | 0x7f7f7f7f) In the inner word iteration, we have to compute the "x | 0x7f7f7f7f" part, so we can reuse that in the above calculation. Once we have this mask, we perform divide and conquer to find the highest 0x80 location. Signed-off-by: David S. Miller --- arch/sparc/lib/usercopy.c | 50 ++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 29 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index 851cb75ce18..87f96453403 100644 --- a/arch/sparc/lib/usercopy.c +++ b/arch/sparc/lib/usercopy.c @@ -11,35 +11,20 @@ EXPORT_SYMBOL(copy_from_user_overflow); #define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) -/* Return the high bit set in the first byte that is a zero */ -static inline unsigned long has_zero(unsigned long a) -{ - return ((a - REPEAT_BYTE(0x01)) & ~a) & REPEAT_BYTE(0x80); -} - -static inline long find_zero(unsigned long c) +static inline long find_zero(unsigned long mask) { + long byte = 0; #ifdef CONFIG_64BIT - if (!(c & 0xff00000000000000UL)) - return 0; - if (!(c & 0x00ff000000000000UL)) - return 1; - if (!(c & 0x0000ff0000000000UL)) - return 2; - if (!(c & 0x000000ff00000000UL)) - return 3; -#define __OFF 4 -#else -#define __OFF 0 + if (mask >> 32) + mask >>= 32; + else + byte = 4; #endif - if (!(c & 0xff000000)) - return __OFF + 0; - if (!(c & 0x00ff0000)) - return __OFF + 1; - if (!(c & 0x0000ff00)) - return __OFF + 2; - return __OFF + 3; -#undef __OFF + if (mask >> 16) + mask >>= 16; + else + byte += 2; + return (mask >> 8) ? byte : byte + 1; } /* @@ -50,6 +35,8 @@ static inline long find_zero(unsigned long c) */ static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) { + const unsigned long high_bits = REPEAT_BYTE(0xfe) + 1; + const unsigned long low_bits = REPEAT_BYTE(0x7f); long res = 0; /* @@ -63,14 +50,19 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long goto byte_at_a_time; while (max >= sizeof(unsigned long)) { - unsigned long c; + unsigned long c, v, rhs; /* Fall back to byte-at-a-time if we get a page fault */ if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) break; + rhs = c | low_bits; + v = (c + high_bits) & ~rhs; *(unsigned long *)(dst+res) = c; - if (has_zero(c)) - return res + find_zero(c); + if (v) { + v = (c & low_bits) + low_bits;; + v = ~(v | rhs); + return res + find_zero(v); + } res += sizeof(unsigned long); max -= sizeof(unsigned long); } -- cgit v1.2.3-70-g09d2 From 35c9646062eba15351c6d64ea0c02176e5cb66ac Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 23 May 2012 19:56:06 -0700 Subject: sparc: Increase portability of strncpy_from_user() implementation. Hide details of maximum user address calculation in a new asm/uaccess.h interface named user_addr_max(). Provide little-endian implementation in find_zero(), which should work but can probably be improved. Abstrace alignment check behind IS_UNALIGNED() macro. Kill double-semicolon, noticed by David Howells. Signed-off-by: David S. Miller --- arch/sparc/include/asm/uaccess.h | 3 +++ arch/sparc/lib/usercopy.c | 32 +++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 5 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/uaccess.h b/arch/sparc/include/asm/uaccess.h index 42a28cfd941..20c2acb0633 100644 --- a/arch/sparc/include/asm/uaccess.h +++ b/arch/sparc/include/asm/uaccess.h @@ -6,6 +6,9 @@ #include #endif +#define user_addr_max() \ + (segment_eq(get_fs(), USER_DS) ? STACK_TOP : ~0UL) + extern long strncpy_from_user(char *dest, const char __user *src, long count); #endif diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index 87f96453403..f61ed820cb6 100644 --- a/arch/sparc/lib/usercopy.c +++ b/arch/sparc/lib/usercopy.c @@ -3,6 +3,8 @@ #include #include +#include + void copy_from_user_overflow(void) { WARN(1, "Buffer overflow detected!\n"); @@ -14,6 +16,8 @@ EXPORT_SYMBOL(copy_from_user_overflow); static inline long find_zero(unsigned long mask) { long byte = 0; + +#ifdef __BIG_ENDIAN #ifdef CONFIG_64BIT if (mask >> 32) mask >>= 32; @@ -25,8 +29,28 @@ static inline long find_zero(unsigned long mask) else byte += 2; return (mask >> 8) ? byte : byte + 1; +#else +#ifdef CONFIG_64BIT + if (!((unsigned int) mask)) { + mask >>= 32; + byte = 4; + } +#endif + if (!(mask & 0xffff)) { + mask >>= 16; + byte += 2; + } + return (mask & 0xff) ? byte : byte + 1; +#endif } +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +#define IS_UNALIGNED(src, dst) 0 +#else +#define IS_UNALIGNED(src, dst) \ + (((long) dst | (long) src) & (sizeof(long) - 1)) +#endif + /* * Do a strncpy, return length of string without final '\0'. * 'count' is the user-supplied count (return 'count' if we @@ -46,7 +70,7 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long if (max > count) max = count; - if (((long) dst | (long) src) & (sizeof(long) - 1)) + if (IS_UNALIGNED(src, dst)) goto byte_at_a_time; while (max >= sizeof(unsigned long)) { @@ -59,7 +83,7 @@ static inline long do_strncpy_from_user(char *dst, const char __user *src, long v = (c + high_bits) & ~rhs; *(unsigned long *)(dst+res) = c; if (v) { - v = (c & low_bits) + low_bits;; + v = (c & low_bits) + low_bits; v = ~(v | rhs); return res + find_zero(v); } @@ -119,9 +143,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count) if (unlikely(count <= 0)) return 0; - max_addr = ~0UL; - if (likely(segment_eq(get_fs(), USER_DS))) - max_addr = STACK_TOP; + max_addr = user_addr_max(); src_addr = (unsigned long)src; if (likely(src_addr < max_addr)) { unsigned long max = max_addr - src_addr; -- cgit v1.2.3-70-g09d2 From 446969084d33a4064a39d280806da642c54ba4ac Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 23 May 2012 20:12:50 -0700 Subject: kernel: Move REPEAT_BYTE definition into linux/kernel.h And make sure that everything using it explicitly includes that header file. Signed-off-by: David S. Miller --- arch/sparc/lib/usercopy.c | 3 +-- arch/x86/include/asm/word-at-a-time.h | 4 ++-- fs/namei.c | 1 + include/linux/kernel.h | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index f61ed820cb6..0b12e91d6cc 100644 --- a/arch/sparc/lib/usercopy.c +++ b/arch/sparc/lib/usercopy.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -11,8 +12,6 @@ void copy_from_user_overflow(void) } EXPORT_SYMBOL(copy_from_user_overflow); -#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) - static inline long find_zero(unsigned long mask) { long byte = 0; diff --git a/arch/x86/include/asm/word-at-a-time.h b/arch/x86/include/asm/word-at-a-time.h index e58f03b206c..ae03facfadd 100644 --- a/arch/x86/include/asm/word-at-a-time.h +++ b/arch/x86/include/asm/word-at-a-time.h @@ -1,6 +1,8 @@ #ifndef _ASM_WORD_AT_A_TIME_H #define _ASM_WORD_AT_A_TIME_H +#include + /* * This is largely generic for little-endian machines, but the * optimal byte mask counting is probably going to be something @@ -35,8 +37,6 @@ static inline long count_masked_bytes(long mask) #endif -#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) - /* Return the high bit set in the first byte that is a zero */ static inline unsigned long has_zero(unsigned long a) { diff --git a/fs/namei.c b/fs/namei.c index f9e883c1b85..8d2ba420e42 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 645231c373c..fbe9bfacb8d 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -38,6 +38,8 @@ #define STACK_MAGIC 0xdeadbeef +#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x)) + #define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) #define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask)) #define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) -- cgit v1.2.3-70-g09d2 From 2922585b93294d47172a765115e0dbc1bfe1be19 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 May 2012 13:12:28 -0700 Subject: lib: Sparc's strncpy_from_user is generic enough, move under lib/ To use this, an architecture simply needs to: 1) Provide a user_addr_max() implementation via asm/uaccess.h 2) Add "select GENERIC_STRNCPY_FROM_USER" to their arch Kcnfig 3) Remove the existing strncpy_from_user() implementation and symbol exports their architecture had. Signed-off-by: David S. Miller Acked-by: David Howells --- arch/sparc/Kconfig | 1 + arch/sparc/lib/usercopy.c | 144 --------------------------------------------- lib/Kconfig | 3 + lib/Makefile | 2 + lib/strncpy_from_user.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 144 deletions(-) create mode 100644 lib/strncpy_from_user.c (limited to 'arch/sparc') diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 051af37f2b4..22474233205 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -32,6 +32,7 @@ config SPARC select HAVE_NMI_WATCHDOG if SPARC64 select HAVE_BPF_JIT select GENERIC_SMP_IDLE_THREAD + select GENERIC_STRNCPY_FROM_USER config SPARC32 def_bool !64BIT diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index 0b12e91d6cc..5c4284ce1c0 100644 --- a/arch/sparc/lib/usercopy.c +++ b/arch/sparc/lib/usercopy.c @@ -1,153 +1,9 @@ #include -#include #include -#include #include -#include - void copy_from_user_overflow(void) { WARN(1, "Buffer overflow detected!\n"); } EXPORT_SYMBOL(copy_from_user_overflow); - -static inline long find_zero(unsigned long mask) -{ - long byte = 0; - -#ifdef __BIG_ENDIAN -#ifdef CONFIG_64BIT - if (mask >> 32) - mask >>= 32; - else - byte = 4; -#endif - if (mask >> 16) - mask >>= 16; - else - byte += 2; - return (mask >> 8) ? byte : byte + 1; -#else -#ifdef CONFIG_64BIT - if (!((unsigned int) mask)) { - mask >>= 32; - byte = 4; - } -#endif - if (!(mask & 0xffff)) { - mask >>= 16; - byte += 2; - } - return (mask & 0xff) ? byte : byte + 1; -#endif -} - -#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS -#define IS_UNALIGNED(src, dst) 0 -#else -#define IS_UNALIGNED(src, dst) \ - (((long) dst | (long) src) & (sizeof(long) - 1)) -#endif - -/* - * Do a strncpy, return length of string without final '\0'. - * 'count' is the user-supplied count (return 'count' if we - * hit it), 'max' is the address space maximum (and we return - * -EFAULT if we hit it). - */ -static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) -{ - const unsigned long high_bits = REPEAT_BYTE(0xfe) + 1; - const unsigned long low_bits = REPEAT_BYTE(0x7f); - long res = 0; - - /* - * Truncate 'max' to the user-specified limit, so that - * we only have one limit we need to check in the loop - */ - if (max > count) - max = count; - - if (IS_UNALIGNED(src, dst)) - goto byte_at_a_time; - - while (max >= sizeof(unsigned long)) { - unsigned long c, v, rhs; - - /* Fall back to byte-at-a-time if we get a page fault */ - if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) - break; - rhs = c | low_bits; - v = (c + high_bits) & ~rhs; - *(unsigned long *)(dst+res) = c; - if (v) { - v = (c & low_bits) + low_bits; - v = ~(v | rhs); - return res + find_zero(v); - } - res += sizeof(unsigned long); - max -= sizeof(unsigned long); - } - -byte_at_a_time: - while (max) { - char c; - - if (unlikely(__get_user(c,src+res))) - return -EFAULT; - dst[res] = c; - if (!c) - return res; - res++; - max--; - } - - /* - * Uhhuh. We hit 'max'. But was that the user-specified maximum - * too? If so, that's ok - we got as much as the user asked for. - */ - if (res >= count) - return res; - - /* - * Nope: we hit the address space limit, and we still had more - * characters the caller would have wanted. That's an EFAULT. - */ - return -EFAULT; -} - -/** - * strncpy_from_user: - Copy a NUL terminated string from userspace. - * @dst: Destination address, in kernel space. This buffer must be at - * least @count bytes long. - * @src: Source address, in user space. - * @count: Maximum number of bytes to copy, including the trailing NUL. - * - * Copies a NUL-terminated string from userspace to kernel space. - * - * On success, returns the length of the string (not including the trailing - * NUL). - * - * If access to userspace fails, returns -EFAULT (some data may have been - * copied). - * - * If @count is smaller than the length of the string, copies @count bytes - * and returns @count. - */ -long strncpy_from_user(char *dst, const char __user *src, long count) -{ - unsigned long max_addr, src_addr; - - if (unlikely(count <= 0)) - return 0; - - max_addr = user_addr_max(); - src_addr = (unsigned long)src; - if (likely(src_addr < max_addr)) { - unsigned long max = max_addr - src_addr; - return do_strncpy_from_user(dst, src, count, max); - } - return -EFAULT; -} -EXPORT_SYMBOL(strncpy_from_user); diff --git a/lib/Kconfig b/lib/Kconfig index 4a8aba2e5cc..49cb46337db 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -16,6 +16,9 @@ config BITREVERSE config RATIONAL boolean +config GENERIC_STRNCPY_FROM_USER + bool + config GENERIC_FIND_FIRST_BIT bool diff --git a/lib/Makefile b/lib/Makefile index 18515f0267c..57538b4d01d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -123,6 +123,8 @@ obj-$(CONFIG_SIGNATURE) += digsig.o obj-$(CONFIG_CLZ_TAB) += clz_tab.o +obj-$(CONFIG_GENERIC_STRNCPY_FROM_USER) += strncpy_from_user.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c new file mode 100644 index 00000000000..c4c09b0e96b --- /dev/null +++ b/lib/strncpy_from_user.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include + +#include + +static inline long find_zero(unsigned long mask) +{ + long byte = 0; + +#ifdef __BIG_ENDIAN +#ifdef CONFIG_64BIT + if (mask >> 32) + mask >>= 32; + else + byte = 4; +#endif + if (mask >> 16) + mask >>= 16; + else + byte += 2; + return (mask >> 8) ? byte : byte + 1; +#else +#ifdef CONFIG_64BIT + if (!((unsigned int) mask)) { + mask >>= 32; + byte = 4; + } +#endif + if (!(mask & 0xffff)) { + mask >>= 16; + byte += 2; + } + return (mask & 0xff) ? byte : byte + 1; +#endif +} + +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +#define IS_UNALIGNED(src, dst) 0 +#else +#define IS_UNALIGNED(src, dst) \ + (((long) dst | (long) src) & (sizeof(long) - 1)) +#endif + +/* + * Do a strncpy, return length of string without final '\0'. + * 'count' is the user-supplied count (return 'count' if we + * hit it), 'max' is the address space maximum (and we return + * -EFAULT if we hit it). + */ +static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) +{ + const unsigned long high_bits = REPEAT_BYTE(0xfe) + 1; + const unsigned long low_bits = REPEAT_BYTE(0x7f); + long res = 0; + + /* + * Truncate 'max' to the user-specified limit, so that + * we only have one limit we need to check in the loop + */ + if (max > count) + max = count; + + if (IS_UNALIGNED(src, dst)) + goto byte_at_a_time; + + while (max >= sizeof(unsigned long)) { + unsigned long c, v, rhs; + + /* Fall back to byte-at-a-time if we get a page fault */ + if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) + break; + rhs = c | low_bits; + v = (c + high_bits) & ~rhs; + *(unsigned long *)(dst+res) = c; + if (v) { + v = (c & low_bits) + low_bits; + v = ~(v | rhs); + return res + find_zero(v); + } + res += sizeof(unsigned long); + max -= sizeof(unsigned long); + } + +byte_at_a_time: + while (max) { + char c; + + if (unlikely(__get_user(c,src+res))) + return -EFAULT; + dst[res] = c; + if (!c) + return res; + res++; + max--; + } + + /* + * Uhhuh. We hit 'max'. But was that the user-specified maximum + * too? If so, that's ok - we got as much as the user asked for. + */ + if (res >= count) + return res; + + /* + * Nope: we hit the address space limit, and we still had more + * characters the caller would have wanted. That's an EFAULT. + */ + return -EFAULT; +} + +/** + * strncpy_from_user: - Copy a NUL terminated string from userspace. + * @dst: Destination address, in kernel space. This buffer must be at + * least @count bytes long. + * @src: Source address, in user space. + * @count: Maximum number of bytes to copy, including the trailing NUL. + * + * Copies a NUL-terminated string from userspace to kernel space. + * + * On success, returns the length of the string (not including the trailing + * NUL). + * + * If access to userspace fails, returns -EFAULT (some data may have been + * copied). + * + * If @count is smaller than the length of the string, copies @count bytes + * and returns @count. + */ +long strncpy_from_user(char *dst, const char __user *src, long count) +{ + unsigned long max_addr, src_addr; + + if (unlikely(count <= 0)) + return 0; + + max_addr = user_addr_max(); + src_addr = (unsigned long)src; + if (likely(src_addr < max_addr)) { + unsigned long max = max_addr - src_addr; + return do_strncpy_from_user(dst, src, count, max); + } + return -EFAULT; +} +EXPORT_SYMBOL(strncpy_from_user); -- cgit v1.2.3-70-g09d2 From c5389831cda3b38a56606a348a537a1332f2d729 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 May 2012 13:41:58 -0700 Subject: sparc: Fix user_addr_max() definition. We need to use TASK_SIZE because for 64-bit tasks the value of STACK_TOP actually sits in the middle of the address space so we'll get false-negatives. Adjust the TASK_SIZE definition on sparc64 to accomodate this, in the context in which user_addr_max() is used we have the test_thread_flag() definition available but not the one for test_tsk_thread_flag(). Signed-off-by: David S. Miller --- arch/sparc/include/asm/processor_64.h | 4 +++- arch/sparc/include/asm/uaccess.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'arch/sparc') diff --git a/arch/sparc/include/asm/processor_64.h b/arch/sparc/include/asm/processor_64.h index e713db24993..6ca7709971d 100644 --- a/arch/sparc/include/asm/processor_64.h +++ b/arch/sparc/include/asm/processor_64.h @@ -42,7 +42,9 @@ #define TASK_SIZE_OF(tsk) \ (test_tsk_thread_flag(tsk,TIF_32BIT) ? \ (1UL << 32UL) : ((unsigned long)-VPTE_SIZE)) -#define TASK_SIZE TASK_SIZE_OF(current) +#define TASK_SIZE \ + (test_thread_flag(TIF_32BIT) ? \ + (1UL << 32UL) : ((unsigned long)-VPTE_SIZE)) #ifdef __KERNEL__ #define STACK_TOP32 ((1UL << 32UL) - PAGE_SIZE) diff --git a/arch/sparc/include/asm/uaccess.h b/arch/sparc/include/asm/uaccess.h index 20c2acb0633..0167d26d0d1 100644 --- a/arch/sparc/include/asm/uaccess.h +++ b/arch/sparc/include/asm/uaccess.h @@ -7,7 +7,7 @@ #endif #define user_addr_max() \ - (segment_eq(get_fs(), USER_DS) ? STACK_TOP : ~0UL) + (segment_eq(get_fs(), USER_DS) ? TASK_SIZE : ~0UL) extern long strncpy_from_user(char *dest, const char __user *src, long count); -- cgit v1.2.3-70-g09d2