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/lib/usercopy.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) (limited to 'arch/sparc/lib/usercopy.c') diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index 14b363fec8a2..851cb75ce189 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 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/lib/usercopy.c') diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index 851cb75ce189..87f96453403a 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 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/lib/usercopy.c') diff --git a/arch/sparc/include/asm/uaccess.h b/arch/sparc/include/asm/uaccess.h index 42a28cfd941f..20c2acb06332 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 87f96453403a..f61ed820cb61 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 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/lib/usercopy.c') diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index f61ed820cb61..0b12e91d6ccc 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 e58f03b206c3..ae03facfadd6 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 f9e883c1b856..8d2ba420e42f 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 645231c373c8..fbe9bfacb8db 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 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/lib/usercopy.c') diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 051af37f2b49..22474233205b 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 0b12e91d6ccc..5c4284ce1c03 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 4a8aba2e5cc0..49cb46337db9 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 18515f0267c4..57538b4d01d2 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 000000000000..c4c09b0e96ba --- /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