proc_destroy() refcnting issues dealt with
[akaros.git] / kern / src / umem.c
1 /* Copyright (c) 2009, 2010 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * Andrew Waterman <waterman@cs.berkeley.edu>
4  * See LICENSE for details.
5  *
6  * Functions for working with userspace's address space.  The user_mem ones need
7  * to involve some form of pinning (TODO), and that global static needs to go. */
8
9 #include <ros/common.h>
10 #include <umem.h>
11 #include <process.h>
12 #include <error.h>
13 #include <kmalloc.h>
14 #include <assert.h>
15 #include <pmap.h>
16 #include <smp.h>
17
18 /**
19  * @brief Global variable used to store erroneous virtual addresses as the
20  *        result of a failed user_mem_check().
21  *
22  * zra: What if two checks fail at the same time? Maybe this should be per-cpu?
23  *
24  */
25 static void *DANGEROUS RACY user_mem_check_addr;
26
27 /**
28  * @brief Check that an environment is allowed to access the range of memory
29  * [va, va+len) with permissions 'perm | PTE_P'.
30  *
31  * Normally 'perm' will contain PTE_U at least, but this is not required.  The
32  * function get_va_perms only checks for PTE_U, PTE_W, and PTE_P.  It won't
33  * check for things like PTE_PS, PTE_A, etc.
34  * 'va' and 'len' need not be page-aligned;
35  *
36  * A user program can access a virtual address if:
37  *     -# the address is below ULIM
38  *     -# the page table gives it permission.  
39  *
40  * If there is an error, 'user_mem_check_addr' is set to the first
41  * erroneous virtual address.
42  *
43  * @param p    the process associated with the user program trying to access
44  *             the virtual address range
45  * @param va   the first virtual address in the range
46  * @param len  the length of the virtual address range
47  * @param perm the permissions the user is trying to access the virtual address 
48  *             range with
49  *
50  * @return VA a pointer of type COUNT(len) to the address range
51  * @return NULL trying to access this range of virtual addresses is not allowed
52  */
53 void *user_mem_check(struct proc *p, const void *DANGEROUS va, size_t len,
54                      int perm)
55 {
56         if (len == 0) {
57                 warn("Called user_mem_check with a len of 0. Don't do that. Returning NULL");
58                 return NULL;
59         }
60         
61         // TODO - will need to sort this out wrt page faulting / PTE_P
62         // also could be issues with sleeping and waking up to find pages
63         // are unmapped, though i think the lab ignores this since the 
64         // kernel is uninterruptible
65         void *DANGEROUS start, *DANGEROUS end;
66         size_t num_pages, i;
67         int page_perms = 0;
68
69         perm |= PTE_P;
70         start = ROUNDDOWN((void*DANGEROUS)va, PGSIZE);
71         end = ROUNDUP((void*DANGEROUS)va + len, PGSIZE);
72         if (start >= end) {
73                 warn("Blimey!  Wrap around in VM range calculation!");  
74                 return NULL;
75         }
76         num_pages = LA2PPN(end - start);
77         for (i = 0; i < num_pages; i++, start += PGSIZE) {
78                 page_perms = get_va_perms(p->env_pgdir, start);
79                 // ensures the bits we want on are turned on.  if not, error out
80                 if ((page_perms & perm) != perm) {
81                         if (i == 0)
82                                 user_mem_check_addr = (void*DANGEROUS)va;
83                         else
84                                 user_mem_check_addr = start;
85                         return NULL;
86                 }
87         }
88         // this should never be needed, since the perms should catch it
89         if ((uintptr_t)end > ULIM) {
90                 warn ("I suck - Bug in user permission mappings!");
91                 return NULL;
92         }
93         return (void *COUNT(len))TC(va);
94 }
95
96 /**
97  * @brief Checks that process 'p' is allowed to access the range
98  * of memory [va, va+len) with permissions 'perm | PTE_U'. Destroy 
99  * process 'p' if the assertion fails.
100  *
101  * This function is identical to user_mem_assert() except that it has a side
102  * affect of destroying the process 'p' if the memory check fails.
103  *
104  * @param p    the process associated with the user program trying to access
105  *             the virtual address range
106  * @param va   the first virtual address in the range
107  * @param len  the length of the virtual address range
108  * @param perm the permissions the user is trying to access the virtual address 
109  *             range with
110  *
111  * @return VA a pointer of type COUNT(len) to the address range
112  * @return NULL trying to access this range of virtual addresses is not allowed
113  *              process 'p' is destroyed
114  *
115  * GIANT WARNING: this could fuck up your refcnting for p if p was an
116  * edible/refcounted reference.  (fix is to return, or just not use this) */
117 void *user_mem_assert(struct proc *p, const void *DANGEROUS va, size_t len,
118                        int perm)
119 {
120         if (len == 0) {
121                 warn("Called user_mem_assert with a len of 0. Don't do that. Returning NULL");
122                 return NULL;
123         }
124         
125         void *COUNT(len) res = user_mem_check(p, va, len, perm | PTE_USER_RO);
126         if (!res) {
127                 cprintf("[%08x] user_mem_check assertion failure for "
128                         "va %08x\n", p->pid, user_mem_check_addr);
129                 /* assuming this is used with an inedible reference */
130                 proc_incref(p, 1);
131                 proc_destroy(p);        // may not return
132                 assert(0);
133         return NULL;
134         }
135     return res;
136 }
137
138 /**
139  * @brief Copies data from a user buffer to a kernel buffer.
140  * 
141  * @param p    the process associated with the user program
142  *             from which the buffer is being copied
143  * @param dest the destination address of the kernel buffer
144  * @param va   the address of the userspace buffer from which we are copying
145  * @param len  the length of the userspace buffer
146  *
147  * @return ESUCCESS on success
148  * @return -EFAULT  the page assocaited with 'va' is not present, the user 
149  *                  lacks the proper permissions, or there was an invalid 'va'
150  */
151 int memcpy_from_user(struct proc *p, void *dest, const void *DANGEROUS va,
152                      size_t len)
153 {
154         const void *DANGEROUS start, *DANGEROUS end;
155         size_t num_pages, i;
156         pte_t *pte;
157         uintptr_t perm = PTE_P | PTE_USER_RO;
158         size_t bytes_copied = 0;
159
160         static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
161
162         start = ROUNDDOWN(va, PGSIZE);
163         end = ROUNDUP(va + len, PGSIZE);
164
165         if (start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
166                 return -EFAULT;
167
168         num_pages = LA2PPN(end - start);
169         for (i = 0; i < num_pages; i++) {
170                 pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0);
171                 if (!pte)
172                         return -EFAULT;
173                 if ((*pte & PTE_P) && (*pte & PTE_USER_RO) != PTE_USER_RO)
174                         return -EFAULT;
175                 if (!(*pte & PTE_P))
176                         if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_READ))
177                                 return -EFAULT;
178
179                 void *kpage = KADDR(PTE_ADDR(*pte));
180                 const void *src_start = i > 0 ? kpage : kpage + (va - start);
181                 void *dst_start = dest + bytes_copied;
182                 size_t copy_len = PGSIZE;
183                 if (i == 0)
184                         copy_len -= va - start;
185                 if (i == num_pages-1)
186                         copy_len -= end - (va + len);
187
188                 memcpy(dst_start, src_start, copy_len);
189                 bytes_copied += copy_len;
190         }
191         assert(bytes_copied == len);
192         return 0;
193 }
194
195 /* Same as above, but sets errno */
196 int memcpy_from_user_errno(struct proc *p, void *dst, const void *src, int len)
197 {
198         if (memcpy_from_user(p, dst, src, len)) {
199                 set_errno(EINVAL);
200                 return -1;
201         }
202         return 0;
203 }
204
205 /**
206  * @brief Copies data to a user buffer from a kernel buffer.
207  * 
208  * @param p    the process associated with the user program
209  *             to which the buffer is being copied
210  * @param dest the destination address of the user buffer
211  * @param va   the address of the kernel buffer from which we are copying
212  * @param len  the length of the user buffer
213  *
214  * @return ESUCCESS on success
215  * @return -EFAULT  the page assocaited with 'va' is not present, the user 
216  *                  lacks the proper permissions, or there was an invalid 'va'
217  */
218 int memcpy_to_user(struct proc *p, void *va, const void *src, size_t len)
219 {
220         const void *DANGEROUS start, *DANGEROUS end;
221         size_t num_pages, i;
222         pte_t *pte;
223         uintptr_t perm = PTE_P | PTE_USER_RW;
224         size_t bytes_copied = 0;
225
226         static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
227
228         start = ROUNDDOWN(va, PGSIZE);
229         end = ROUNDUP(va + len, PGSIZE);
230
231         if (start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
232                 return -EFAULT;
233
234         num_pages = LA2PPN(end - start);
235         for (i = 0; i < num_pages; i++) {
236                 pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0);
237                 if (!pte)
238                         return -EFAULT;
239                 if ((*pte & PTE_P) && (*pte & PTE_USER_RW) != PTE_USER_RW)
240                         return -EFAULT;
241                 if (!(*pte & PTE_P))
242                         if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_WRITE))
243                                 return -EFAULT;
244                 void *kpage = KADDR(PTE_ADDR(*pte));
245                 void *dst_start = i > 0 ? kpage : kpage + (va - start);
246                 const void *src_start = src + bytes_copied;
247                 size_t copy_len = PGSIZE;
248                 if (i == 0)
249                         copy_len -= va - start;
250                 if (i == num_pages - 1)
251                         copy_len -= end - (va + len);
252                 memcpy(dst_start, src_start, copy_len);
253                 bytes_copied += copy_len;
254         }
255         assert(bytes_copied == len);
256         return 0;
257 }
258
259 /* Same as above, but sets errno */
260 int memcpy_to_user_errno(struct proc *p, void *dst, const void *src, int len)
261 {
262         if (memcpy_to_user(p, dst, src, len)) {
263                 set_errno(EINVAL);
264                 return -1;
265         }
266         return 0;
267 }
268
269 /* Creates a buffer (kmalloc) and safely copies into it from va.  Can return an
270  * error code.  Check its response with IS_ERR().  Must be paired with
271  * user_memdup_free() if this succeeded. */
272 void *user_memdup(struct proc *p, const void *va, int len)
273 {
274         void* kva = NULL;
275         if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
276                 return ERR_PTR(-ENOMEM);
277         if (memcpy_from_user(p, kva, va, len)) {
278                 kfree(kva);
279                 return ERR_PTR(-EINVAL);
280         }
281         return kva;
282 }
283
284 void *user_memdup_errno(struct proc *p, const void *va, int len)
285 {
286         void *kva = user_memdup(p, va, len);
287         if (IS_ERR(kva)) {
288                 set_errno(-PTR_ERR(kva));
289                 return NULL;
290         }
291         return kva;
292 }
293
294 void user_memdup_free(struct proc *p, void *va)
295 {
296         kfree(va);
297 }
298
299 /* Same as memdup, but just does strings, and needs to know the actual strlen.
300  * Still needs memdup_free()d.  This will enforce that the string is null
301  * terminated.  The parameter strlen does not include the \0, though it can if
302  * someone else is playing it safe.  Since strlen() doesn't count the \0, we'll
303  * play it safe here. */
304 char *user_strdup(struct proc *p, const char *u_string, size_t strlen)
305 {
306         char *k_string = user_memdup(p, u_string, strlen + 1);
307         if (!IS_ERR(k_string))
308                 k_string[strlen] = '\0';
309         return k_string;
310 }
311
312 /* user_strdup, but this handles the errno.  0 on failure, ptr on success */
313 char *user_strdup_errno(struct proc *p, const char *u_string, size_t strlen)
314 {
315         void *k_string = user_strdup(p, u_string, strlen);
316         if (IS_ERR(k_string)) {
317                 set_errno(-PTR_ERR(k_string));
318                 return NULL;
319         }
320         return k_string;
321 }
322
323 void *kmalloc_errno(int len)
324 {
325         void *kva = NULL;
326         if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
327                 set_errno(ENOMEM);
328         return kva;
329 }
330
331 /* Returns true if uva and kva both resolve to the same phys addr.  If uva is
332  * unmapped, it will return FALSE.  This is probably what you want, since after
333  * all uva isn't kva. */
334 bool uva_is_kva(struct proc *p, void *uva, void *kva)
335 {
336         struct page *u_page;
337         assert(kva);                            /* catch bugs */
338         /* Check offsets first */
339         if (PGOFF(uva) != PGOFF(kva))
340                 return FALSE;
341         /* Check to see if it is the same physical page */
342         u_page = page_lookup(p->env_pgdir, uva, 0);
343         if (!u_page)
344                 return FALSE;
345         return (kva2page(kva) == u_page) ? TRUE : FALSE;
346 }