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 retval = sos_thread_prepare_user_space_access(NULL,
051 (sos_vaddr_t) && catch_pgflt);
052 if (SOS_OK != retval)
053 return retval;
054
055
056
057
058
059
060 for (cptr_dst = (char*)dest,
061 cptr_src = (const char*)src,
062 transfer_sz = size ;
063 transfer_sz > 0 ;
064 cptr_dst++, cptr_src++, transfer_sz--)
065 *cptr_dst = *cptr_src;
066
067 retval = sos_thread_end_user_space_access();
068 if (SOS_OK != retval)
069 return retval;
070
071 return size;
072
073
074 catch_pgflt:
075 {
076 struct sos_thread * cur_thr = sos_thread_get_current();
077 sos_uaddr_t faulted_uaddr = cur_thr->fixup_uaccess.faulted_uaddr;
078
079 if (transfer_from_user)
080 {
081 if ( (faulted_uaddr < src) || (faulted_uaddr - size > src) )
082 sos_display_fatal_error("Unexpected read access in user space");
083 retval = faulted_uaddr - src;
084 }
085 else
086 {
087 if ( (faulted_uaddr < dest) || (faulted_uaddr - size > dest) )
088 sos_display_fatal_error("Unexpected write access in user space");
089 retval = faulted_uaddr - dest;
090 }
091
092 sos_thread_end_user_space_access();
093 return retval;
094 }
095 }
096
097
098 sos_ret_t sos_memcpy_from_user(sos_vaddr_t kernel_to,
099 sos_uaddr_t user_from,
100 sos_size_t size)
101 {
102
103 if (user_from < SOS_PAGING_BASE_USER_ADDRESS)
104 return -SOS_EPERM;
105
106 if (user_from > SOS_PAGING_TOP_USER_ADDRESS - size)
107 return -SOS_EPERM;
108
109 return nocheck_user_memcpy(kernel_to, user_from, size, TRUE);
110 }
111
112
113 sos_ret_t sos_memcpy_to_user(sos_uaddr_t user_to,
114 sos_vaddr_t kernel_from,
115 sos_size_t size)
116 {
117
118 if (user_to < SOS_PAGING_BASE_USER_ADDRESS)
119 return -SOS_EPERM;
120
121 if (user_to > SOS_PAGING_TOP_USER_ADDRESS - size)
122 return -SOS_EPERM;
123
124 return nocheck_user_memcpy(user_to, kernel_from, size, FALSE);
125 }
126
127
128 sos_ret_t sos_strnlen_from_user(sos_uaddr_t user_str, sos_size_t max_len)
129 {
130 __label__ catch_pgflt;
131 const char *sc;
132 sos_ret_t retval;
133
134 KEEP_LABEL(catch_pgflt);
135
136
137 if (user_str < SOS_PAGING_BASE_USER_ADDRESS)
138 return -SOS_EPERM;
139
140
141 if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
142 return -SOS_EINVAL;
143
144 retval = sos_thread_prepare_user_space_access(NULL,
145 (sos_vaddr_t) && catch_pgflt);
146 if (SOS_OK != retval)
147 return retval;
148
149
150
151
152
153
154 for (sc = (const char *)user_str; max_len-- && *sc != '\0'; ++sc)
155 continue;
156
157
158 retval = sos_thread_end_user_space_access();
159 if (SOS_OK != retval)
160 return retval;
161
162 return ((sos_uaddr_t)sc - user_str);
163
164
165 catch_pgflt:
166 {
167 sos_thread_end_user_space_access();
168 return -SOS_EFAULT;
169 }
170 }
171
172
173 static sos_ret_t nocheck_user_strzcpy(char *dst, const char *src,
174 sos_size_t len)
175 {
176 __label__ catch_pgflt;
177 unsigned int i;
178 sos_ret_t retval;
179
180 KEEP_LABEL(catch_pgflt);
181
182 retval = sos_thread_prepare_user_space_access(NULL,
183 (sos_vaddr_t) && catch_pgflt);
184 if (SOS_OK != retval)
185 return retval;
186
187
188
189
190
191
192 for (i = 0; i < len; i++)
193 {
194 dst[i] = src[i];
195 if(src[i] == '\0')
196 break;
197 }
198
199 dst[len-1] = '\0';
200
201 retval = sos_thread_end_user_space_access();
202 if (SOS_OK != retval)
203 return retval;
204
205 return SOS_OK;
206
207
208 catch_pgflt:
209 {
210 sos_thread_end_user_space_access();
211 return -SOS_EFAULT;
212 }
213 }
214
215
216 sos_ret_t sos_strzcpy_from_user(char *kernel_to, sos_uaddr_t user_from,
217 sos_size_t max_len)
218 {
219
220 if ((sos_uaddr_t)user_from < SOS_PAGING_BASE_USER_ADDRESS)
221 return -SOS_EPERM;
222
223
224 if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
225 return -SOS_EINVAL;
226
227 return nocheck_user_strzcpy(kernel_to, (const char*)user_from, max_len);
228 }
229
230
231 sos_ret_t sos_strzcpy_to_user(sos_uaddr_t user_to, const char *kernel_from,
232 sos_size_t max_len)
233 {
234
235 if ((sos_uaddr_t)user_to < SOS_PAGING_BASE_USER_ADDRESS)
236 return -SOS_EPERM;
237
238
239 if ( (max_len < 1) || (max_len > SOS_PAGING_USER_SPACE_SIZE) )
240 return -SOS_EINVAL;
241
242 return nocheck_user_strzcpy((char*)user_to, kernel_from, max_len);
243 }
244
245
246 sos_ret_t sos_strndup_from_user(char ** kernel_to, sos_uaddr_t from_user,
247 sos_size_t max_len,
248 sos_ui32_t kmalloc_flags)
249 {
250 sos_ret_t retval = sos_strnlen_from_user(from_user, max_len);
251 if (retval < 0)
252 return retval;
253
254 *kernel_to = (char*)sos_kmalloc(retval + 1, kmalloc_flags);
255 if (NULL == *kernel_to)
256 return -SOS_ENOMEM;
257
258 retval = sos_strzcpy_from_user(*kernel_to, from_user, retval + 1);
259 if (SOS_OK != retval)
260 {
261 sos_kfree((sos_vaddr_t)*kernel_to);
262 *kernel_to = NULL;
263 }
264
265 return retval;
266 }