001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019 #include <hwcore/paging.h>
020 #include <sos/thread.h>
021 #include <sos/assert.h>
022 #include <sos/kmalloc.h>
023
024 #include "uaccess.h"
025
026
027 int __uaccess_zero_fool_gcc = 0;
028
029
030
031
032
033 #define KEEP_LABEL(lbl) \
034 ({ if (__uaccess_zero_fool_gcc) goto lbl; })
035
036
037 static sos_ret_t nocheck_user_memcpy(sos_vaddr_t dest,
038 sos_vaddr_t src,
039 sos_size_t size,
040 sos_bool_t transfer_from_user)
041 {
042 __label__ catch_pgflt;
043 char *cptr_dst;
044 const char *cptr_src;
045 sos_size_t transfer_sz;
046 sos_ret_t retval;
047
048 KEEP_LABEL(catch_pgflt);
049
050 if (size <= 0)
051 return 0;
052
053 retval = sos_thread_prepare_user_space_access(NULL,
054 (sos_vaddr_t) && catch_pgflt);
055 if (SOS_OK != retval)
056 return retval;
057
058
059
060
061
062
063 for (cptr_dst = (char*)dest,
064 cptr_src = (const char*)src,
065 transfer_sz = size ;
066 transfer_sz > 0 ;
067 cptr_dst++, cptr_src++, transfer_sz--)
068 *cptr_dst = *cptr_src;
069
070 retval = sos_thread_end_user_space_access();
071 if (SOS_OK != retval)
072 return retval;
073
074 return size;
075
076
077 catch_pgflt:
078 {
079 struct sos_thread * cur_thr = sos_thread_get_current();
080 sos_uaddr_t faulted_uaddr = cur_thr->fixup_uaccess.faulted_uaddr;
081
082 if (transfer_from_user)
083 {
084 if ( (faulted_uaddr < src) || (faulted_uaddr - size > src) )
085 sos_display_fatal_error("Unexpected read access in user space");
086 retval = faulted_uaddr - src;
087 }
088 else
089 {
090 if ( (faulted_uaddr < dest) || (faulted_uaddr - size > dest) )
091 sos_display_fatal_error("Unexpected write access in user space");
092 retval = faulted_uaddr - dest;
093 }
094
095 sos_thread_end_user_space_access();
096 return retval;
097 }
098 }
099
100
101 sos_ret_t sos_memcpy_from_user(sos_vaddr_t kernel_to,
102 sos_uaddr_t user_from,
103 sos_size_t size)
104 {
105
106 if (user_from < SOS_PAGING_BASE_USER_ADDRESS)
107 return -SOS_EPERM;
108
109 if (user_from > SOS_PAGING_TOP_USER_ADDRESS - size)
110 return -SOS_EPERM;
111
112 return nocheck_user_memcpy(kernel_to, user_from, size, TRUE);
113 }
114
115
116 sos_ret_t sos_memdup_from_user(sos_vaddr_t * kernel_to, sos_uaddr_t from_user,
117 sos_size_t length,
118 sos_ui32_t kmalloc_flags)
119 {
120 sos_ret_t retval;
121
122 if (length <= 0)
123 return -SOS_EINVAL;
124
125 *kernel_to = sos_kmalloc(length, kmalloc_flags);
126 if (NULL == (void*) *kernel_to)
127 return -SOS_ENOMEM;
128
129 retval = sos_memcpy_from_user(*kernel_to, from_user, length);
130 if ((sos_ret_t)length != retval)
131 {
132 sos_kfree((sos_vaddr_t)*kernel_to);
133 *kernel_to = (sos_vaddr_t) NULL;
134 retval = -SOS_EFAULT;
135 }
136 else
137 retval = SOS_OK;
138
139 return retval;
140 }
141
142
143 sos_ret_t sos_memcpy_to_user(sos_uaddr_t user_to,
144 sos_vaddr_t kernel_from,
145 sos_size_t size)
146 {
147
148 if (user_to < SOS_PAGING_BASE_USER_ADDRESS)
149 return -SOS_EPERM;
150
151 if (user_to > SOS_PAGING_TOP_USER_ADDRESS - size)
152 return -SOS_EPERM;
153
154 return nocheck_user_memcpy(user_to, kernel_from, size, FALSE);
155 }
156
157
158 sos_ret_t sos_strnlen_from_user(sos_uaddr_t user_str, sos_size_t max_len)
159 {
160 __label__ catch_pgflt;
161 const char *sc;
162 sos_ret_t retval;
163
164 KEEP_LABEL(catch_pgflt);
165
166 if (max_len <= 0)
167 return 0;
168
169
170 if (user_str < SOS_PAGING_BASE_USER_ADDRESS)
171 return -SOS_EPERM;
172
173
174 if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
175 return -SOS_EINVAL;
176
177 retval = sos_thread_prepare_user_space_access(NULL,
178 (sos_vaddr_t) && catch_pgflt);
179 if (SOS_OK != retval)
180 return retval;
181
182
183
184
185
186
187 for (sc = (const char *)user_str; max_len-- && *sc != '\0'; ++sc)
188 continue;
189
190
191 retval = sos_thread_end_user_space_access();
192 if (SOS_OK != retval)
193 return retval;
194
195 return ((sos_uaddr_t)sc - user_str);
196
197
198 catch_pgflt:
199 {
200 sos_thread_end_user_space_access();
201 return -SOS_EFAULT;
202 }
203 }
204
205
206 static sos_ret_t nocheck_user_strzcpy(char *dst, const char *src,
207 sos_size_t len)
208 {
209 __label__ catch_pgflt;
210 unsigned int i;
211 sos_ret_t retval;
212
213 KEEP_LABEL(catch_pgflt);
214
215 if (len <= 0)
216 return 0;
217
218 retval = sos_thread_prepare_user_space_access(NULL,
219 (sos_vaddr_t) && catch_pgflt);
220 if (SOS_OK != retval)
221 return retval;
222
223
224
225
226
227
228 for (i = 0; i < len; i++)
229 {
230 dst[i] = src[i];
231 if(src[i] == '\0')
232 break;
233 }
234
235 dst[len-1] = '\0';
236
237 retval = sos_thread_end_user_space_access();
238 if (SOS_OK != retval)
239 return retval;
240
241 return SOS_OK;
242
243
244 catch_pgflt:
245 {
246 sos_thread_end_user_space_access();
247 return -SOS_EFAULT;
248 }
249 }
250
251
252 sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from,
253 sos_size_t max_len)
254 {
255
256 if ((sos_uaddr_t)user_from < SOS_PAGING_BASE_USER_ADDRESS)
257 return -SOS_EPERM;
258
259
260 if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
261 return -SOS_EINVAL;
262
263 return nocheck_user_strzcpy(kernel_to, (const char*)user_from, max_len);
264 }
265
266
267 sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from,
268 sos_size_t max_len)
269 {
270
271 if ((sos_uaddr_t)user_to < SOS_PAGING_BASE_USER_ADDRESS)
272 return -SOS_EPERM;
273
274
275 if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
276 return -SOS_EINVAL;
277
278 return nocheck_user_strzcpy((char*)user_to, kernel_from, max_len);
279 }
280
281
282 sos_ret_t sos_strndup_from_user(char ** kernel_to, sos_uaddr_t from_user,
283 sos_size_t max_len,
284 sos_ui32_t kmalloc_flags)
285 {
286 sos_ret_t retval = sos_strnlen_from_user(from_user, max_len);
287 if (retval < 0)
288 return retval;
289
290 *kernel_to = (char*)sos_kmalloc(retval + 1, kmalloc_flags);
291 if (NULL == *kernel_to)
292 return -SOS_ENOMEM;
293
294 retval = sos_strzcpy_from_user(*kernel_to, from_user, retval + 1);
295 if (SOS_OK != retval)
296 {
297 sos_kfree((sos_vaddr_t)*kernel_to);
298 *kernel_to = NULL;
299 }
300
301 return retval;
302 }
303
304
305 sos_ret_t sos_memcpy_generic_to(sos_genaddr_t to_addr,
306 sos_vaddr_t kernel_from,
307 sos_size_t size)
308 {
309 if (to_addr.is_user)
310 return sos_memcpy_to_user(to_addr.addr, kernel_from, size);
311 memcpy((void*)to_addr.addr, (const void*)kernel_from, size);
312 return size;
313 }
314
315
316 sos_ret_t sos_memcpy_generic_from(sos_vaddr_t kernel_from,
317 sos_genaddr_t from_addr,
318 sos_size_t size)
319 {
320 if (from_addr.is_user)
321 return sos_memcpy_from_user(kernel_from, from_addr.addr, size);
322 memcpy((void*)kernel_from, (const void*)from_addr.addr, size);
323 return size;
324 }