diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2018-01-07 13:06:15 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-01-07 13:06:15 -0500 |
commit | 50fd2f298bef9d1f69ac755f1fdf70cd98746be2 (patch) | |
tree | b7c3dd1d018f7f754d15dc793e4120d0f0c8f007 | |
parent | 6c2c97a24f096e3239bc54029b808c6bcba4f358 (diff) | |
download | linux-stable-50fd2f298bef9d1f69ac755f1fdf70cd98746be2.tar.gz linux-stable-50fd2f298bef9d1f69ac755f1fdf70cd98746be2.tar.bz2 linux-stable-50fd2f298bef9d1f69ac755f1fdf70cd98746be2.zip |
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.h | 1 | ||||
-rw-r--r-- | mm/util.c | 29 |
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 |