]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | /* SPDX-License-Identifier: GPL-2.0 */ |
eed417dd AB |
2 | #ifndef __ASM_GENERIC_UACCESS_H |
3 | #define __ASM_GENERIC_UACCESS_H | |
4 | ||
5 | /* | |
6 | * User space memory access functions, these should work | |
0a4a6647 | 7 | * on any machine that has kernel and user data in the same |
eed417dd AB |
8 | * address space, e.g. all NOMMU machines. |
9 | */ | |
eed417dd AB |
10 | #include <linux/string.h> |
11 | ||
12 | #include <asm/segment.h> | |
13 | ||
14 | #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) | |
15 | ||
16 | #ifndef KERNEL_DS | |
17 | #define KERNEL_DS MAKE_MM_SEG(~0UL) | |
18 | #endif | |
19 | ||
20 | #ifndef USER_DS | |
21 | #define USER_DS MAKE_MM_SEG(TASK_SIZE - 1) | |
22 | #endif | |
23 | ||
24 | #ifndef get_fs | |
25 | #define get_ds() (KERNEL_DS) | |
26 | #define get_fs() (current_thread_info()->addr_limit) | |
27 | ||
28 | static inline void set_fs(mm_segment_t fs) | |
29 | { | |
30 | current_thread_info()->addr_limit = fs; | |
31 | } | |
32 | #endif | |
33 | ||
10a6007b | 34 | #ifndef segment_eq |
eed417dd | 35 | #define segment_eq(a, b) ((a).seg == (b).seg) |
10a6007b | 36 | #endif |
eed417dd | 37 | |
eed417dd AB |
38 | #define access_ok(type, addr, size) __access_ok((unsigned long)(addr),(size)) |
39 | ||
40 | /* | |
41 | * The architecture should really override this if possible, at least | |
42 | * doing a check on the get_fs() | |
43 | */ | |
44 | #ifndef __access_ok | |
45 | static inline int __access_ok(unsigned long addr, unsigned long size) | |
46 | { | |
47 | return 1; | |
48 | } | |
49 | #endif | |
50 | ||
eed417dd AB |
51 | /* |
52 | * These are the main single-value transfer routines. They automatically | |
53 | * use the right size if we just have the right pointer type. | |
54 | * This version just falls back to copy_{from,to}_user, which should | |
55 | * provide a fast-path for small values. | |
56 | */ | |
57 | #define __put_user(x, ptr) \ | |
58 | ({ \ | |
59 | __typeof__(*(ptr)) __x = (x); \ | |
60 | int __pu_err = -EFAULT; \ | |
61 | __chk_user_ptr(ptr); \ | |
62 | switch (sizeof (*(ptr))) { \ | |
63 | case 1: \ | |
64 | case 2: \ | |
65 | case 4: \ | |
66 | case 8: \ | |
67 | __pu_err = __put_user_fn(sizeof (*(ptr)), \ | |
68 | ptr, &__x); \ | |
69 | break; \ | |
70 | default: \ | |
71 | __put_user_bad(); \ | |
72 | break; \ | |
73 | } \ | |
74 | __pu_err; \ | |
75 | }) | |
76 | ||
77 | #define put_user(x, ptr) \ | |
78 | ({ \ | |
1985296a | 79 | void __user *__p = (ptr); \ |
e0acd0bd | 80 | might_fault(); \ |
a02613a4 | 81 | access_ok(VERIFY_WRITE, __p, sizeof(*ptr)) ? \ |
1985296a | 82 | __put_user((x), ((__typeof__(*(ptr)) __user *)__p)) : \ |
eed417dd AB |
83 | -EFAULT; \ |
84 | }) | |
85 | ||
05d88a49 VG |
86 | #ifndef __put_user_fn |
87 | ||
eed417dd AB |
88 | static inline int __put_user_fn(size_t size, void __user *ptr, void *x) |
89 | { | |
d597580d | 90 | return unlikely(raw_copy_to_user(ptr, x, size)) ? -EFAULT : 0; |
eed417dd AB |
91 | } |
92 | ||
05d88a49 VG |
93 | #define __put_user_fn(sz, u, k) __put_user_fn(sz, u, k) |
94 | ||
95 | #endif | |
96 | ||
eed417dd AB |
97 | extern int __put_user_bad(void) __attribute__((noreturn)); |
98 | ||
99 | #define __get_user(x, ptr) \ | |
100 | ({ \ | |
101 | int __gu_err = -EFAULT; \ | |
102 | __chk_user_ptr(ptr); \ | |
103 | switch (sizeof(*(ptr))) { \ | |
104 | case 1: { \ | |
c1aad8dc | 105 | unsigned char __x = 0; \ |
eed417dd AB |
106 | __gu_err = __get_user_fn(sizeof (*(ptr)), \ |
107 | ptr, &__x); \ | |
108 | (x) = *(__force __typeof__(*(ptr)) *) &__x; \ | |
109 | break; \ | |
110 | }; \ | |
111 | case 2: { \ | |
c1aad8dc | 112 | unsigned short __x = 0; \ |
eed417dd AB |
113 | __gu_err = __get_user_fn(sizeof (*(ptr)), \ |
114 | ptr, &__x); \ | |
115 | (x) = *(__force __typeof__(*(ptr)) *) &__x; \ | |
116 | break; \ | |
117 | }; \ | |
118 | case 4: { \ | |
c1aad8dc | 119 | unsigned int __x = 0; \ |
eed417dd AB |
120 | __gu_err = __get_user_fn(sizeof (*(ptr)), \ |
121 | ptr, &__x); \ | |
122 | (x) = *(__force __typeof__(*(ptr)) *) &__x; \ | |
123 | break; \ | |
124 | }; \ | |
125 | case 8: { \ | |
c1aad8dc | 126 | unsigned long long __x = 0; \ |
eed417dd AB |
127 | __gu_err = __get_user_fn(sizeof (*(ptr)), \ |
128 | ptr, &__x); \ | |
129 | (x) = *(__force __typeof__(*(ptr)) *) &__x; \ | |
130 | break; \ | |
131 | }; \ | |
132 | default: \ | |
133 | __get_user_bad(); \ | |
134 | break; \ | |
135 | } \ | |
136 | __gu_err; \ | |
137 | }) | |
138 | ||
139 | #define get_user(x, ptr) \ | |
140 | ({ \ | |
1985296a | 141 | const void __user *__p = (ptr); \ |
e0acd0bd | 142 | might_fault(); \ |
a02613a4 | 143 | access_ok(VERIFY_READ, __p, sizeof(*ptr)) ? \ |
1985296a | 144 | __get_user((x), (__typeof__(*(ptr)) __user *)__p) :\ |
9ad18b75 | 145 | ((x) = (__typeof__(*(ptr)))0,-EFAULT); \ |
eed417dd AB |
146 | }) |
147 | ||
05d88a49 | 148 | #ifndef __get_user_fn |
eed417dd AB |
149 | static inline int __get_user_fn(size_t size, const void __user *ptr, void *x) |
150 | { | |
d597580d | 151 | return unlikely(raw_copy_from_user(x, ptr, size)) ? -EFAULT : 0; |
eed417dd AB |
152 | } |
153 | ||
05d88a49 VG |
154 | #define __get_user_fn(sz, u, k) __get_user_fn(sz, u, k) |
155 | ||
156 | #endif | |
157 | ||
eed417dd AB |
158 | extern int __get_user_bad(void) __attribute__((noreturn)); |
159 | ||
eed417dd AB |
160 | /* |
161 | * Copy a null terminated string from userspace. | |
162 | */ | |
163 | #ifndef __strncpy_from_user | |
164 | static inline long | |
165 | __strncpy_from_user(char *dst, const char __user *src, long count) | |
166 | { | |
167 | char *tmp; | |
168 | strncpy(dst, (const char __force *)src, count); | |
169 | for (tmp = dst; *tmp && count > 0; tmp++, count--) | |
170 | ; | |
171 | return (tmp - dst); | |
172 | } | |
173 | #endif | |
174 | ||
175 | static inline long | |
176 | strncpy_from_user(char *dst, const char __user *src, long count) | |
177 | { | |
a9ede5b3 | 178 | if (!access_ok(VERIFY_READ, src, 1)) |
eed417dd AB |
179 | return -EFAULT; |
180 | return __strncpy_from_user(dst, src, count); | |
181 | } | |
182 | ||
183 | /* | |
184 | * Return the size of a string (including the ending 0) | |
185 | * | |
186 | * Return 0 on exception, a value greater than N if too long | |
187 | */ | |
7f509a9e | 188 | #ifndef __strnlen_user |
830f5800 | 189 | #define __strnlen_user(s, n) (strnlen((s), (n)) + 1) |
7f509a9e G |
190 | #endif |
191 | ||
830f5800 MS |
192 | /* |
193 | * Unlike strnlen, strnlen_user includes the nul terminator in | |
194 | * its returned count. Callers should check for a returned value | |
195 | * greater than N as an indication the string is too long. | |
196 | */ | |
eed417dd AB |
197 | static inline long strnlen_user(const char __user *src, long n) |
198 | { | |
9844813f MF |
199 | if (!access_ok(VERIFY_READ, src, 1)) |
200 | return 0; | |
7f509a9e | 201 | return __strnlen_user(src, n); |
eed417dd | 202 | } |
eed417dd | 203 | |
eed417dd AB |
204 | /* |
205 | * Zero Userspace | |
206 | */ | |
207 | #ifndef __clear_user | |
208 | static inline __must_check unsigned long | |
209 | __clear_user(void __user *to, unsigned long n) | |
210 | { | |
211 | memset((void __force *)to, 0, n); | |
212 | return 0; | |
213 | } | |
214 | #endif | |
215 | ||
216 | static inline __must_check unsigned long | |
217 | clear_user(void __user *to, unsigned long n) | |
218 | { | |
e0acd0bd | 219 | might_fault(); |
a9ede5b3 | 220 | if (!access_ok(VERIFY_WRITE, to, n)) |
eed417dd AB |
221 | return n; |
222 | ||
223 | return __clear_user(to, n); | |
224 | } | |
225 | ||
aaa2e7ac AV |
226 | #include <asm/extable.h> |
227 | ||
eed417dd | 228 | #endif /* __ASM_GENERIC_UACCESS_H */ |