diff options
Diffstat (limited to 'lib/usercopy.c')
-rw-r--r-- | lib/usercopy.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/lib/usercopy.c b/lib/usercopy.c index c2bfbcaeb3dc..cbb4d9ec00f2 100644 --- a/lib/usercopy.c +++ b/lib/usercopy.c | |||
@@ -1,5 +1,6 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/uaccess.h> | 2 | #include <linux/uaccess.h> |
3 | #include <linux/bitops.h> | ||
3 | 4 | ||
4 | /* out-of-line parts */ | 5 | /* out-of-line parts */ |
5 | 6 | ||
@@ -31,3 +32,57 @@ unsigned long _copy_to_user(void __user *to, const void *from, unsigned long n) | |||
31 | } | 32 | } |
32 | EXPORT_SYMBOL(_copy_to_user); | 33 | EXPORT_SYMBOL(_copy_to_user); |
33 | #endif | 34 | #endif |
35 | |||
36 | /** | ||
37 | * check_zeroed_user: check if a userspace buffer only contains zero bytes | ||
38 | * @from: Source address, in userspace. | ||
39 | * @size: Size of buffer. | ||
40 | * | ||
41 | * This is effectively shorthand for "memchr_inv(from, 0, size) == NULL" for | ||
42 | * userspace addresses (and is more efficient because we don't care where the | ||
43 | * first non-zero byte is). | ||
44 | * | ||
45 | * Returns: | ||
46 | * * 0: There were non-zero bytes present in the buffer. | ||
47 | * * 1: The buffer was full of zero bytes. | ||
48 | * * -EFAULT: access to userspace failed. | ||
49 | */ | ||
50 | int check_zeroed_user(const void __user *from, size_t size) | ||
51 | { | ||
52 | unsigned long val; | ||
53 | uintptr_t align = (uintptr_t) from % sizeof(unsigned long); | ||
54 | |||
55 | if (unlikely(size == 0)) | ||
56 | return 1; | ||
57 | |||
58 | from -= align; | ||
59 | size += align; | ||
60 | |||
61 | if (!user_access_begin(from, size)) | ||
62 | return -EFAULT; | ||
63 | |||
64 | unsafe_get_user(val, (unsigned long __user *) from, err_fault); | ||
65 | if (align) | ||
66 | val &= ~aligned_byte_mask(align); | ||
67 | |||
68 | while (size > sizeof(unsigned long)) { | ||
69 | if (unlikely(val)) | ||
70 | goto done; | ||
71 | |||
72 | from += sizeof(unsigned long); | ||
73 | size -= sizeof(unsigned long); | ||
74 | |||
75 | unsafe_get_user(val, (unsigned long __user *) from, err_fault); | ||
76 | } | ||
77 | |||
78 | if (size < sizeof(unsigned long)) | ||
79 | val &= aligned_byte_mask(size); | ||
80 | |||
81 | done: | ||
82 | user_access_end(); | ||
83 | return (val == 0); | ||
84 | err_fault: | ||
85 | user_access_end(); | ||
86 | return -EFAULT; | ||
87 | } | ||
88 | EXPORT_SYMBOL(check_zeroed_user); | ||