Blame view

include/asm-generic/uaccess.h 7.39 KB
eed417ddd   Arnd Bergmann   asm-generic: add ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  #ifndef __ASM_GENERIC_UACCESS_H
  #define __ASM_GENERIC_UACCESS_H
  
  /*
   * User space memory access functions, these should work
   * on a ny machine that has kernel and user data in the same
   * address space, e.g. all NOMMU machines.
   */
  #include <linux/sched.h>
  #include <linux/mm.h>
  #include <linux/string.h>
  
  #include <asm/segment.h>
  
  #define MAKE_MM_SEG(s)	((mm_segment_t) { (s) })
  
  #ifndef KERNEL_DS
  #define KERNEL_DS	MAKE_MM_SEG(~0UL)
  #endif
  
  #ifndef USER_DS
  #define USER_DS		MAKE_MM_SEG(TASK_SIZE - 1)
  #endif
  
  #ifndef get_fs
  #define get_ds()	(KERNEL_DS)
  #define get_fs()	(current_thread_info()->addr_limit)
  
  static inline void set_fs(mm_segment_t fs)
  {
  	current_thread_info()->addr_limit = fs;
  }
  #endif
  
  #define segment_eq(a, b) ((a).seg == (b).seg)
  
  #define VERIFY_READ	0
  #define VERIFY_WRITE	1
  
  #define access_ok(type, addr, size) __access_ok((unsigned long)(addr),(size))
  
  /*
   * The architecture should really override this if possible, at least
   * doing a check on the get_fs()
   */
  #ifndef __access_ok
  static inline int __access_ok(unsigned long addr, unsigned long size)
  {
  	return 1;
  }
  #endif
  
  /*
   * The exception table consists of pairs of addresses: the first is the
   * address of an instruction that is allowed to fault, and the second is
   * the address at which the program should continue.  No registers are
   * modified, so it is entirely up to the continuation code to figure out
   * what to do.
   *
   * All the routines below use bits of fixup code that are out of line
   * with the main instruction path.  This means when everything is well,
   * we don't even have to jump over them.  Further, they do not intrude
   * on our cache or tlb entries.
   */
  
  struct exception_table_entry
  {
  	unsigned long insn, fixup;
  };
  
  /* Returns 0 if exception not found and fixup otherwise.  */
  extern unsigned long search_exception_table(unsigned long);
  
  /*
   * architectures with an MMU should override these two
   */
  #ifndef __copy_from_user
  static inline __must_check long __copy_from_user(void *to,
  		const void __user * from, unsigned long n)
  {
  	if (__builtin_constant_p(n)) {
  		switch(n) {
  		case 1:
  			*(u8 *)to = *(u8 __force *)from;
  			return 0;
  		case 2:
  			*(u16 *)to = *(u16 __force *)from;
  			return 0;
  		case 4:
  			*(u32 *)to = *(u32 __force *)from;
  			return 0;
  #ifdef CONFIG_64BIT
  		case 8:
  			*(u64 *)to = *(u64 __force *)from;
  			return 0;
  #endif
  		default:
  			break;
  		}
  	}
  
  	memcpy(to, (const void __force *)from, n);
  	return 0;
  }
  #endif
  
  #ifndef __copy_to_user
  static inline __must_check long __copy_to_user(void __user *to,
  		const void *from, unsigned long n)
  {
  	if (__builtin_constant_p(n)) {
  		switch(n) {
  		case 1:
  			*(u8 __force *)to = *(u8 *)from;
  			return 0;
  		case 2:
  			*(u16 __force *)to = *(u16 *)from;
  			return 0;
  		case 4:
  			*(u32 __force *)to = *(u32 *)from;
  			return 0;
  #ifdef CONFIG_64BIT
  		case 8:
  			*(u64 __force *)to = *(u64 *)from;
  			return 0;
  #endif
  		default:
  			break;
  		}
  	}
  
  	memcpy((void __force *)to, from, n);
  	return 0;
  }
  #endif
  
  /*
   * These are the main single-value transfer routines.  They automatically
   * use the right size if we just have the right pointer type.
   * This version just falls back to copy_{from,to}_user, which should
   * provide a fast-path for small values.
   */
  #define __put_user(x, ptr) \
  ({								\
  	__typeof__(*(ptr)) __x = (x);				\
  	int __pu_err = -EFAULT;					\
          __chk_user_ptr(ptr);                                    \
  	switch (sizeof (*(ptr))) {				\
  	case 1:							\
  	case 2:							\
  	case 4:							\
  	case 8:							\
  		__pu_err = __put_user_fn(sizeof (*(ptr)),	\
  					 ptr, &__x);		\
  		break;						\
  	default:						\
  		__put_user_bad();				\
  		break;						\
  	 }							\
  	__pu_err;						\
  })
  
  #define put_user(x, ptr)					\
  ({								\
  	might_sleep();						\
a9ede5b35   Mike Frysinger   asm-generic: uacc...
166
  	access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)) ?		\
eed417ddd   Arnd Bergmann   asm-generic: add ...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  		__put_user(x, ptr) :				\
  		-EFAULT;					\
  })
  
  static inline int __put_user_fn(size_t size, void __user *ptr, void *x)
  {
  	size = __copy_to_user(ptr, x, size);
  	return size ? -EFAULT : size;
  }
  
  extern int __put_user_bad(void) __attribute__((noreturn));
  
  #define __get_user(x, ptr)					\
  ({								\
  	int __gu_err = -EFAULT;					\
  	__chk_user_ptr(ptr);					\
  	switch (sizeof(*(ptr))) {				\
  	case 1: {						\
  		unsigned char __x;				\
  		__gu_err = __get_user_fn(sizeof (*(ptr)),	\
  					 ptr, &__x);		\
  		(x) = *(__force __typeof__(*(ptr)) *) &__x;	\
  		break;						\
  	};							\
  	case 2: {						\
  		unsigned short __x;				\
  		__gu_err = __get_user_fn(sizeof (*(ptr)),	\
  					 ptr, &__x);		\
  		(x) = *(__force __typeof__(*(ptr)) *) &__x;	\
  		break;						\
  	};							\
  	case 4: {						\
  		unsigned int __x;				\
  		__gu_err = __get_user_fn(sizeof (*(ptr)),	\
  					 ptr, &__x);		\
  		(x) = *(__force __typeof__(*(ptr)) *) &__x;	\
  		break;						\
  	};							\
  	case 8: {						\
  		unsigned long long __x;				\
  		__gu_err = __get_user_fn(sizeof (*(ptr)),	\
  					 ptr, &__x);		\
  		(x) = *(__force __typeof__(*(ptr)) *) &__x;	\
  		break;						\
  	};							\
  	default:						\
  		__get_user_bad();				\
  		break;						\
  	}							\
  	__gu_err;						\
  })
  
  #define get_user(x, ptr)					\
  ({								\
  	might_sleep();						\
a9ede5b35   Mike Frysinger   asm-generic: uacc...
222
  	access_ok(VERIFY_READ, ptr, sizeof(*ptr)) ?		\
eed417ddd   Arnd Bergmann   asm-generic: add ...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
  		__get_user(x, ptr) :				\
  		-EFAULT;					\
  })
  
  static inline int __get_user_fn(size_t size, const void __user *ptr, void *x)
  {
  	size = __copy_from_user(x, ptr, size);
  	return size ? -EFAULT : size;
  }
  
  extern int __get_user_bad(void) __attribute__((noreturn));
  
  #ifndef __copy_from_user_inatomic
  #define __copy_from_user_inatomic __copy_from_user
  #endif
  
  #ifndef __copy_to_user_inatomic
  #define __copy_to_user_inatomic __copy_to_user
  #endif
  
  static inline long copy_from_user(void *to,
  		const void __user * from, unsigned long n)
  {
  	might_sleep();
a9ede5b35   Mike Frysinger   asm-generic: uacc...
247
  	if (access_ok(VERIFY_READ, from, n))
eed417ddd   Arnd Bergmann   asm-generic: add ...
248
249
250
251
252
253
254
255
256
  		return __copy_from_user(to, from, n);
  	else
  		return n;
  }
  
  static inline long copy_to_user(void __user *to,
  		const void *from, unsigned long n)
  {
  	might_sleep();
a9ede5b35   Mike Frysinger   asm-generic: uacc...
257
  	if (access_ok(VERIFY_WRITE, to, n))
eed417ddd   Arnd Bergmann   asm-generic: add ...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
  		return __copy_to_user(to, from, n);
  	else
  		return n;
  }
  
  /*
   * Copy a null terminated string from userspace.
   */
  #ifndef __strncpy_from_user
  static inline long
  __strncpy_from_user(char *dst, const char __user *src, long count)
  {
  	char *tmp;
  	strncpy(dst, (const char __force *)src, count);
  	for (tmp = dst; *tmp && count > 0; tmp++, count--)
  		;
  	return (tmp - dst);
  }
  #endif
  
  static inline long
  strncpy_from_user(char *dst, const char __user *src, long count)
  {
a9ede5b35   Mike Frysinger   asm-generic: uacc...
281
  	if (!access_ok(VERIFY_READ, src, 1))
eed417ddd   Arnd Bergmann   asm-generic: add ...
282
283
284
285
286
287
288
289
290
  		return -EFAULT;
  	return __strncpy_from_user(dst, src, count);
  }
  
  /*
   * Return the size of a string (including the ending 0)
   *
   * Return 0 on exception, a value greater than N if too long
   */
7f509a9ef   GuanXuetao   asm-generic heade...
291
  #ifndef __strnlen_user
830f5800a   Mark Salter   fix default __str...
292
  #define __strnlen_user(s, n) (strnlen((s), (n)) + 1)
7f509a9ef   GuanXuetao   asm-generic heade...
293
  #endif
830f5800a   Mark Salter   fix default __str...
294
295
296
297
298
  /*
   * Unlike strnlen, strnlen_user includes the nul terminator in
   * its returned count. Callers should check for a returned value
   * greater than N as an indication the string is too long.
   */
eed417ddd   Arnd Bergmann   asm-generic: add ...
299
300
  static inline long strnlen_user(const char __user *src, long n)
  {
9844813f2   Mike Frysinger   asm-generic: uacc...
301
302
  	if (!access_ok(VERIFY_READ, src, 1))
  		return 0;
7f509a9ef   GuanXuetao   asm-generic heade...
303
  	return __strnlen_user(src, n);
eed417ddd   Arnd Bergmann   asm-generic: add ...
304
  }
eed417ddd   Arnd Bergmann   asm-generic: add ...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  
  static inline long strlen_user(const char __user *src)
  {
  	return strnlen_user(src, 32767);
  }
  
  /*
   * Zero Userspace
   */
  #ifndef __clear_user
  static inline __must_check unsigned long
  __clear_user(void __user *to, unsigned long n)
  {
  	memset((void __force *)to, 0, n);
  	return 0;
  }
  #endif
  
  static inline __must_check unsigned long
  clear_user(void __user *to, unsigned long n)
  {
  	might_sleep();
a9ede5b35   Mike Frysinger   asm-generic: uacc...
327
  	if (!access_ok(VERIFY_WRITE, to, n))
eed417ddd   Arnd Bergmann   asm-generic: add ...
328
329
330
331
332
333
  		return n;
  
  	return __clear_user(to, n);
  }
  
  #endif /* __ASM_GENERIC_UACCESS_H */