summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@plumgrid.com>2015-08-28 15:56:22 -0700
committerDavid S. Miller <davem@davemloft.net>2015-08-28 16:27:27 -0700
commit1a6877b9c0c2ad901d4335d909432d3bb6d3a330 (patch)
tree25b3a1577a82199d1100834c1bbf6cefad34a5a7 /lib
parentc9fd56b34efd06a031cbc918d288f09e38173ff9 (diff)
downloadlinux-1a6877b9c0c2ad901d4335d909432d3bb6d3a330.tar.bz2
lib: introduce strncpy_from_unsafe()
generalize FETCH_FUNC_NAME(memory, string) into strncpy_from_unsafe() and fix sparse warnings that were present in original implementation. Signed-off-by: Alexei Starovoitov <ast@plumgrid.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'lib')
-rw-r--r--lib/strncpy_from_user.c41
1 files changed, 41 insertions, 0 deletions
diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c
index e0af6ff73d14..ead8c4a068d1 100644
--- a/lib/strncpy_from_user.c
+++ b/lib/strncpy_from_user.c
@@ -112,3 +112,44 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
return -EFAULT;
}
EXPORT_SYMBOL(strncpy_from_user);
+
+/**
+ * strncpy_from_unsafe: - Copy a NUL terminated string from unsafe address.
+ * @dst: Destination address, in kernel space. This buffer must be at
+ * least @count bytes long.
+ * @src: Unsafe address.
+ * @count: Maximum number of bytes to copy, including the trailing NUL.
+ *
+ * Copies a NUL-terminated string from unsafe address to kernel buffer.
+ *
+ * On success, returns the length of the string INCLUDING the trailing NUL.
+ *
+ * If access fails, returns -EFAULT (some data may have been copied
+ * and the trailing NUL added).
+ *
+ * If @count is smaller than the length of the string, copies @count-1 bytes,
+ * sets the last byte of @dst buffer to NUL and returns @count.
+ */
+long strncpy_from_unsafe(char *dst, const void *unsafe_addr, long count)
+{
+ mm_segment_t old_fs = get_fs();
+ const void *src = unsafe_addr;
+ long ret;
+
+ if (unlikely(count <= 0))
+ return 0;
+
+ set_fs(KERNEL_DS);
+ pagefault_disable();
+
+ do {
+ ret = __copy_from_user_inatomic(dst++,
+ (const void __user __force *)src++, 1);
+ } while (dst[-1] && ret == 0 && src - unsafe_addr < count);
+
+ dst[-1] = '\0';
+ pagefault_enable();
+ set_fs(old_fs);
+
+ return ret < 0 ? ret : src - unsafe_addr;
+}