summaryrefslogtreecommitdiffstats
path: root/mm/util.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2015-12-24 06:06:05 +0100
committerAl Viro <viro@zeniv.linux.org.uk>2016-01-04 16:20:19 +0100
commite9d408e107db9a554b36c3a79f67b37dd3e16da0 (patch)
tree97383e40e388aa302828ffb2ddd845b257109118 /mm/util.c
parentLinux 4.4-rc7 (diff)
downloadlinux-e9d408e107db9a554b36c3a79f67b37dd3e16da0.tar.xz
linux-e9d408e107db9a554b36c3a79f67b37dd3e16da0.zip
new helper: memdup_user_nul()
Similar to memdup_user(), except that allocated buffer is one byte longer and '\0' is stored after the copied data. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'mm/util.c')
-rw-r--r--mm/util.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/mm/util.c b/mm/util.c
index 9af1c12b310c..2d28f7930043 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -176,6 +176,37 @@ char *strndup_user(const char __user *s, long n)
}
EXPORT_SYMBOL(strndup_user);
+/**
+ * memdup_user_nul - duplicate memory region from user space and NUL-terminate
+ *
+ * @src: source address in user space
+ * @len: number of bytes to copy
+ *
+ * Returns an ERR_PTR() on failure.
+ */
+void *memdup_user_nul(const void __user *src, size_t len)
+{
+ char *p;
+
+ /*
+ * Always use GFP_KERNEL, since copy_from_user() can sleep and
+ * cause pagefault, which makes it pointless to use GFP_NOFS
+ * or GFP_ATOMIC.
+ */
+ p = kmalloc_track_caller(len + 1, GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ if (copy_from_user(p, src, len)) {
+ kfree(p);
+ return ERR_PTR(-EFAULT);
+ }
+ p[len] = '\0';
+
+ return p;
+}
+EXPORT_SYMBOL(memdup_user_nul);
+
void __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma,
struct vm_area_struct *prev, struct rb_node *rb_parent)
{