summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2018-01-07 13:06:15 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2018-01-07 13:06:15 -0500
commit50fd2f298bef9d1f69ac755f1fdf70cd98746be2 (patch)
treeb7c3dd1d018f7f754d15dc793e4120d0f0c8f007
parent6c2c97a24f096e3239bc54029b808c6bcba4f358 (diff)
downloadlinux-50fd2f298bef9d1f69ac755f1fdf70cd98746be2.tar.bz2
new primitive: vmemdup_user()
similar to memdup_user(), but does *not* guarantee that result will be physically contiguous; use only in cases where that's not a requirement and free it with kvfree(). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--include/linux/string.h1
-rw-r--r--mm/util.c29
2 files changed, 29 insertions, 1 deletions
diff --git a/include/linux/string.h b/include/linux/string.h
index 410ecf17de3c..12d5429de0c8 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -11,6 +11,7 @@
extern char *strndup_user(const char __user *, long);
extern void *memdup_user(const void __user *, size_t);
+extern void *vmemdup_user(const void __user *, size_t);
extern void *memdup_user_nul(const void __user *, size_t);
/*
diff --git a/mm/util.c b/mm/util.c
index 4b93ffa6df96..c1250501364f 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -150,7 +150,8 @@ EXPORT_SYMBOL(kmemdup_nul);
* @src: source address in user space
* @len: number of bytes to copy
*
- * Returns an ERR_PTR() on failure.
+ * Returns an ERR_PTR() on failure. Result is physically
+ * contiguous, to be freed by kfree().
*/
void *memdup_user(const void __user *src, size_t len)
{
@@ -169,6 +170,32 @@ void *memdup_user(const void __user *src, size_t len)
}
EXPORT_SYMBOL(memdup_user);
+/**
+ * vmemdup_user - duplicate memory region from user space
+ *
+ * @src: source address in user space
+ * @len: number of bytes to copy
+ *
+ * Returns an ERR_PTR() on failure. Result may be not
+ * physically contiguous. Use kvfree() to free.
+ */
+void *vmemdup_user(const void __user *src, size_t len)
+{
+ void *p;
+
+ p = kvmalloc(len, GFP_USER);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ if (copy_from_user(p, src, len)) {
+ kvfree(p);
+ return ERR_PTR(-EFAULT);
+ }
+
+ return p;
+}
+EXPORT_SYMBOL(vmemdup_user);
+
/*
* strndup_user - duplicate an existing string from user space
* @s: The string to duplicate