Reorganized user-memory checking/copying functions
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 26 Jul 2010 20:52:58 +0000 (13:52 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:49 +0000 (17:35 -0700)
Put them all in one place.  If I missed any, move them to umem.{h,c}.

kern/include/error.h
kern/include/frontend.h
kern/include/pmap.h
kern/include/syscall.h
kern/include/umem.h [new file with mode: 0644]
kern/src/Makefrag
kern/src/frontend.c
kern/src/pmap.c
kern/src/syscall.c
kern/src/umem.c [new file with mode: 0644]
kern/src/vfs.c

index cbc1774..3aa8c3f 100644 (file)
@@ -7,6 +7,10 @@
 
 typedef int error_t;
 
+#define ERR_PTR(err)  ((void *)((uintptr_t)(err)))
+#define PTR_ERR(ptr)  ((uintptr_t)(ptr))
+#define IS_ERR(ptr)   ((uintptr_t)-(uintptr_t)(ptr) < 512)
+
 /* The special format for printk %e takes an integer
  * error code and prints a string describing the error.
  * The integer may be positive or negative,
index 2026bc3..2f1f4dd 100644 (file)
@@ -24,14 +24,6 @@ int32_t frontend_syscall(pid_t pid, int32_t syscall_num,
 int frontend_syscall_errno(struct proc* p, int n, int a0, 
                            int a1, int a2, int a3);
 
-void* user_memdup(struct proc* p, const void* va, int len);
-void* user_memdup_errno(struct proc* p, const void* va, int len);
-void user_memdup_free(struct proc* p, void* va);
-char* user_strdup(struct proc* p, const char* va0, int max);
-char* user_strdup_errno(struct proc* p, const char* va, int max);
-int memcpy_to_user_errno(struct proc* p, void* dst, const void* src, int len);
-void* kmalloc_errno(int len);
-
 void file_init(void);
 error_t file_read_page(struct file* f, physaddr_t pa, size_t pgoff);
 struct file* file_open(const char* path, int oflag, int mode);
index 63128b0..a43b5b0 100644 (file)
@@ -5,8 +5,6 @@
  * Copyright (c) 2009 The Regents of the University of California
  * Barret Rhoden <brho@cs.berkeley.edu>
  * Kevin Klues <klueska@cs.berkeley.edu> (multiboot functions)
- * Andrew Waterman <waterman@cs.berkeley.edu> (memcpy_to/from_user)
- * Zach Anderson (zra@cs.berkeley.edu> (user_mem_strlcpy)
  * See LICENSE for details.
  *
  * Physical memory mangement, low-level virtual address space initialization and
@@ -79,24 +77,6 @@ void setup_default_mtrrs(barrier_t* smp_barrier);
 void   tlb_invalidate(pde_t *COUNT(NPDENTRIES) pgdir, void *SNT va);
 void tlb_flush_global(void);
 
-/* TODO: either move these, or make them take a pgdir */
-void * (DALLOC(len) user_mem_check) (env_t *env, const void *DANGEROUS va,
-                                     size_t len, int perm);
-
-void * (DALLOC(len) user_mem_assert)(env_t *env, const void *DANGEROUS va,
-                                     size_t len, int perm);
-
-size_t user_mem_strlcpy(env_t *env, char *NT COUNT(len - 1),
-                        const char *DANGEROUS va, size_t len, int perm);
-
-error_t
-memcpy_from_user(env_t* env, void* COUNT(len) dest,
-                 const void *DANGEROUS va, size_t len);
-
-error_t
-memcpy_to_user(env_t* env, void*DANGEROUS va,
-                 const void *COUNT(len) src, size_t len);
-                 
 /* Arch specific implementations for these */
 pte_t *pgdir_walk(pde_t *COUNT(NPDENTRIES) pgdir, const void *SNT va, int create);
 int get_va_perms(pde_t *COUNT(NPDENTRIES) pgdir, const void *SNT va);
index ef6396d..b890f43 100644 (file)
@@ -7,10 +7,6 @@
 #include <ros/syscall.h>
 #include <process.h>
 
-#define ERR_PTR(err)  ((void *)((uintptr_t)(err)))
-#define PTR_ERR(ptr)  ((uintptr_t)(ptr))
-#define IS_ERR(ptr)   ((uintptr_t)-(uintptr_t)(ptr) < 512)
-
 #define SYSTRACE_ON                                    0x01
 #define SYSTRACE_LOUD                          0x02
 #define SYSTRACE_ALLPROC                       0x04
diff --git a/kern/include/umem.h b/kern/include/umem.h
new file mode 100644 (file)
index 0000000..15837b5
--- /dev/null
@@ -0,0 +1,38 @@
+/* Copyright (c) 2009, 2010 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * Andrew Waterman <waterman@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Functions for working with userspace's address space. */
+
+#include <ros/common.h>
+#include <process.h>
+
+/* Can they use the area in the manner of perm? */
+void *user_mem_check(struct proc *p, const void *DANGEROUS va, size_t len,
+                     int perm);
+/* Kills them if they can't use the area in the manner of perm */
+void *user_mem_assert(struct proc *p, const void *DANGEROUS va, size_t len, 
+                      int perm);
+
+/* Copy from proc p into the kernel's dest from src */
+int memcpy_from_user(struct proc *p, void *dest, const void *DANGEROUS va,
+                     size_t len);
+
+/* Copy to proc p into va from the kernel's src */
+int memcpy_to_user(struct proc *p, void *DANGEROUS va, const void *src,
+                   size_t len);
+/* Same as above, but sets errno */
+int memcpy_to_user_errno(struct proc *p, void *dst, const void *src, int len);
+                 
+/* Creates a buffer (kmalloc) and safely copies into it from va.  Can return an
+ * error code.  Check its response with IS_ERR().  Must be paired with
+ * user_memdup_free() if this succeeded. */
+void *user_memdup(struct proc *p, const void *va, int len);
+/* Same as above, but sets errno */
+void *user_memdup_errno(struct proc *p, const void *va, int len);
+void user_memdup_free(struct proc *p, void *va);
+/* Same as memdup, but just does strings.  still needs memdup_freed */
+char *user_strdup(struct proc *p, const char *va0, int max);
+char *user_strdup_errno(struct proc *p, const char *va, int max);
+void *kmalloc_errno(int len);
index d3feaa5..6497799 100644 (file)
@@ -42,6 +42,7 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/frontend.c \
                  $(KERN_SRC_DIR)/vfs.c \
                  $(KERN_SRC_DIR)/radix.c \
+                 $(KERN_SRC_DIR)/umem.c \
                  $(KERN_SRC_DIR)/testing.c
 
 # Only build files if they exist.
index 17e5c17..970f22d 100644 (file)
@@ -39,97 +39,6 @@ frontend_proc_free(struct proc *SAFE p)
 #endif
 }
 
-void* user_memdup(struct proc* p, const void* va, int len)
-{
-       void* kva = NULL;
-       if(len < 0 || (kva = kmalloc(len,0)) == NULL)
-               return ERR_PTR(-ENOMEM);
-       if(memcpy_from_user(p,kva,va,len))
-       {
-               kfree(kva);
-               return ERR_PTR(-EINVAL);
-       }
-
-       return kva;
-}
-
-void* user_memdup_errno(struct proc* p, const void* va, int len)
-{
-       void* kva = user_memdup(p,va,len);
-       if(IS_ERR(kva))
-       {
-               set_errno(current_tf,-PTR_ERR(kva));
-               return NULL;
-       }
-       return kva;
-}
-
-void user_memdup_free(struct proc* p, void* va)
-{
-       kfree(va);
-}
-
-char* user_strdup(struct proc* p, const char* va0, int max)
-{
-       max++;
-       char* kbuf = (char*)kmalloc(PGSIZE,0);
-       if(kbuf == NULL)
-               return ERR_PTR(-ENOMEM);
-
-       int pos = 0, len = 0;
-       const char* va = va0;
-       while(max > 0 && len == 0)
-       {
-               int thislen = MIN(PGSIZE-(uintptr_t)va%PGSIZE,max);
-               if(memcpy_from_user(p,kbuf,va,thislen))
-               {
-                       kfree(kbuf);
-                       return ERR_PTR(-EINVAL);
-               }
-
-               const char* nullterm = memchr(kbuf,0,thislen);
-               if(nullterm)
-                       len = pos+(nullterm-kbuf)+1;
-
-               pos += thislen;
-               va += thislen;
-               max -= thislen;
-       }
-
-       kfree(kbuf);
-       return len ? user_memdup(p,va0,len) : ERR_PTR(-EINVAL);
-}
-
-char* user_strdup_errno(struct proc* p, const char* va, int max)
-{
-       void* kva = user_strdup(p,va,max);
-       if(IS_ERR(kva))
-       {
-               set_errno(current_tf,-PTR_ERR(kva));
-               return NULL;
-       }
-       return kva;
-}
-
-int memcpy_to_user_errno(struct proc* p, void* dst, const void* src,
-                                int len)
-{
-       if(memcpy_to_user(p,dst,src,len))
-       {
-               set_errno(current_tf,EINVAL);
-               return -1;
-       }
-       return 0;
-}
-
-void* kmalloc_errno(int len)
-{
-       void* kva = NULL;
-       if(len < 0 || (kva = kmalloc(len,0)) == NULL)
-               set_errno(current_tf,ENOMEM);
-       return kva;
-}
-
 struct kmem_cache* struct_file_cache;
 void file_init()
 {
index 7d88581..e202fc7 100644 (file)
 #include <stdio.h>
 #include <mm.h>
 
-/**
- * @brief Global variable used to store erroneous virtual addresses as the
- *        result of a failed user_mem_check().
- *
- * zra: What if two checks fail at the same time? Maybe this should be per-cpu?
- *
- */
-static void *DANGEROUS RACY user_mem_check_addr;
-
 volatile uint32_t vpt_lock = 0;
 volatile uint32_t vpd_lock = 0;
 
@@ -260,271 +251,3 @@ void tlb_invalidate(pde_t *pgdir, void *va)
        // For now, there is only one address space, so always invalidate.
        invlpg(va);
 }
-
-/**
- * @brief Check that an environment is allowed to access the range of memory
- * [va, va+len) with permissions 'perm | PTE_P'.
- *
- * Normally 'perm' will contain PTE_U at least, but this is not required.  The
- * function get_va_perms only checks for PTE_U, PTE_W, and PTE_P.  It won't
- * check for things like PTE_PS, PTE_A, etc.
- * 'va' and 'len' need not be page-aligned;
- *
- * A user program can access a virtual address if:
- *     -# the address is below ULIM
- *     -# the page table gives it permission.  
- *
- * If there is an error, 'user_mem_check_addr' is set to the first
- * erroneous virtual address.
- *
- * @param env  the environment associated with the user program trying to access
- *             the virtual address range
- * @param va   the first virtual address in the range
- * @param len  the length of the virtual address range
- * @param perm the permissions the user is trying to access the virtual address 
- *             range with
- *
- * @return VA a pointer of type COUNT(len) to the address range
- * @return NULL trying to access this range of virtual addresses is not allowed
- */
-void* user_mem_check(env_t *env, const void *DANGEROUS va, size_t len, int perm)
-{
-       if (len == 0) {
-               warn("Called user_mem_check with a len of 0. Don't do that. Returning NULL");
-               return NULL;
-       }
-       
-       // TODO - will need to sort this out wrt page faulting / PTE_P
-       // also could be issues with sleeping and waking up to find pages
-       // are unmapped, though i think the lab ignores this since the 
-       // kernel is uninterruptible
-       void *DANGEROUS start, *DANGEROUS end;
-       size_t num_pages, i;
-       int page_perms = 0;
-
-       perm |= PTE_P;
-       start = ROUNDDOWN((void*DANGEROUS)va, PGSIZE);
-       end = ROUNDUP((void*DANGEROUS)va + len, PGSIZE);
-       if (start >= end) {
-               warn("Blimey!  Wrap around in VM range calculation!");  
-               return NULL;
-       }
-       num_pages = LA2PPN(end - start);
-       for (i = 0; i < num_pages; i++, start += PGSIZE) {
-               page_perms = get_va_perms(env->env_pgdir, start);
-               // ensures the bits we want on are turned on.  if not, error out
-               if ((page_perms & perm) != perm) {
-                       if (i == 0)
-                               user_mem_check_addr = (void*DANGEROUS)va;
-                       else
-                               user_mem_check_addr = start;
-                       return NULL;
-               }
-       }
-       // this should never be needed, since the perms should catch it
-       if ((uintptr_t)end > ULIM) {
-               warn ("I suck - Bug in user permission mappings!");
-               return NULL;
-       }
-       return (void *COUNT(len))TC(va);
-}
-
-/**
- * @brief Use the kernel to copy a string from a buffer stored in userspace
- *        to a buffer stored elsewhere in the address space (potentially in 
- *        memory only accessible by the kernel)
- *
- * @param env  the environment associated with the user program from which
- *             the string is being copied
- * @param dst  the destination of the buffer into which the string 
- *             is being copied
- * @param va   the start address of the buffer where the string resides
- * @param len  the length of the buffer 
- * @param perm the permissions with which the user is trying to access 
- *             elements of the original buffer 
- *
- * @return LEN the length of the new buffer copied into 'dst'
- */
-size_t
-user_mem_strlcpy(env_t *env, char *_dst, const char *DANGEROUS va,
-                 size_t _len, int perm)
-{
-       const char *DANGEROUS src = va;
-       size_t len = _len;
-       char *NT COUNT(_len-1) dst_in = _dst;
-       char *NT BND(_dst,_dst + _len - 1) dst = _dst;
-
-       if (len > 0) {
-               while (1) {
-                       char *c;
-                       // what if len was 1?
-                       if (--len <= 0) break;
-                       c = user_mem_check(env, src, 1, perm);
-                       if (!c) break;
-                       if (*c == '\0') break;
-                       // TODO: ivy bitches about this
-                       *dst++ = *c;
-                       src++;
-               }
-               *dst = '\0';
-       }
-
-       return dst - dst_in;
-}
-
-/**
- * @brief Checks that environment 'env' is allowed to access the range
- * of memory [va, va+len) with permissions 'perm | PTE_U'. Destroy 
- * environment 'env' if the assertion fails.
- *
- * This function is identical to user_mem_assert() except that it has a side
- * affect of destroying the environment 'env' if the memory check fails.
- *
- * @param env  the environment associated with the user program trying to access
- *             the virtual address range
- * @param va   the first virtual address in the range
- * @param len  the length of the virtual address range
- * @param perm the permissions the user is trying to access the virtual address 
- *             range with
- *
- * @return VA a pointer of type COUNT(len) to the address range
- * @return NULL trying to access this range of virtual addresses is not allowed
- *              environment 'env' is destroyed
- */
-void *
-user_mem_assert(env_t *env, const void *DANGEROUS va, size_t len, int perm)
-{
-       if (len == 0) {
-               warn("Called user_mem_assert with a len of 0. Don't do that. Returning NULL");
-               return NULL;
-       }
-       
-       void *COUNT(len) res = user_mem_check(env,va,len,perm | PTE_USER_RO);
-       if (!res) {
-               cprintf("[%08x] user_mem_check assertion failure for "
-                       "va %08x\n", env->pid, user_mem_check_addr);
-               proc_destroy(env);      // may not return
-        return NULL;
-       }
-    return res;
-}
-
-/**
- * @brief Copies data from a user buffer to a kernel buffer.
- * 
- * @param env  the environment associated with the user program
- *             from which the buffer is being copied
- * @param dest the destination address of the kernel buffer
- * @param va   the address of the userspace buffer from which we are copying
- * @param len  the length of the userspace buffer
- *
- * @return ESUCCESS on success
- * @return -EFAULT  the page assocaited with 'va' is not present, the user 
- *                  lacks the proper permissions, or there was an invalid 'va'
- */
-error_t memcpy_from_user(env_t* env, void* COUNT(len) dest,
-                 const void *DANGEROUS va, size_t len)
-{
-       const void *DANGEROUS start, *DANGEROUS end;
-       size_t num_pages, i;
-       pte_t *pte;
-       uintptr_t perm = PTE_P | PTE_USER_RO;
-       size_t bytes_copied = 0;
-
-       static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
-
-       start = ROUNDDOWN(va, PGSIZE);
-       end = ROUNDUP(va + len, PGSIZE);
-
-       if(start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
-               return -EFAULT;
-
-       num_pages = LA2PPN(end - start);
-       for(i = 0; i < num_pages; i++)
-       {
-               pte = pgdir_walk(env->env_pgdir, start+i*PGSIZE, 0);
-               if(!pte)
-                       return -EFAULT;
-               if((*pte & PTE_P) && (*pte & PTE_USER_RO) != PTE_USER_RO)
-                       return -EFAULT;
-               if(!(*pte & PTE_P))
-                       if(handle_page_fault(env,(uintptr_t)start+i*PGSIZE,PROT_READ))
-                               return -EFAULT;
-
-               void*COUNT(PGSIZE) kpage = KADDR(PTE_ADDR(*pte));
-               const void* src_start = i > 0 ? kpage : kpage+(va-start);
-               void* dst_start = dest+bytes_copied;
-               size_t copy_len = PGSIZE;
-               if(i == 0)
-                       copy_len -= va-start;
-               if(i == num_pages-1)
-                       copy_len -= end-(va+len);
-
-               memcpy(dst_start,src_start,copy_len);
-               bytes_copied += copy_len;
-       }
-
-       assert(bytes_copied == len);
-
-       return ESUCCESS;
-}
-
-/**
- * @brief Copies data to a user buffer from a kernel buffer.
- * 
- * @param env  the environment associated with the user program
- *             to which the buffer is being copied
- * @param dest the destination address of the user buffer
- * @param va   the address of the kernel buffer from which we are copying
- * @param len  the length of the user buffer
- *
- * @return ESUCCESS on success
- * @return -EFAULT  the page assocaited with 'va' is not present, the user 
- *                  lacks the proper permissions, or there was an invalid 'va'
- */
-error_t memcpy_to_user(env_t* env, void*DANGEROUS va,
-                 const void *COUNT(len) src, size_t len)
-{
-       const void *DANGEROUS start, *DANGEROUS end;
-       size_t num_pages, i;
-       pte_t *pte;
-       uintptr_t perm = PTE_P | PTE_USER_RW;
-       size_t bytes_copied = 0;
-
-       static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
-
-       start = ROUNDDOWN(va, PGSIZE);
-       end = ROUNDUP(va + len, PGSIZE);
-
-       if(start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
-               return -EFAULT;
-
-       num_pages = LA2PPN(end - start);
-       for(i = 0; i < num_pages; i++)
-       {
-               pte = pgdir_walk(env->env_pgdir, start+i*PGSIZE, 0);
-               if(!pte)
-                       return -EFAULT;
-               if((*pte & PTE_P) && (*pte & PTE_USER_RW) != PTE_USER_RW)
-                       return -EFAULT;
-               if(!(*pte & PTE_P))
-                       if(handle_page_fault(env,(uintptr_t)start+i*PGSIZE,PROT_WRITE))
-                               return -EFAULT;
-
-               void*COUNT(PGSIZE) kpage = KADDR(PTE_ADDR(*pte));
-               void* dst_start = i > 0 ? kpage : kpage+(va-start);
-               const void* src_start = src+bytes_copied;
-               size_t copy_len = PGSIZE;
-               if(i == 0)
-                       copy_len -= va-start;
-               if(i == num_pages-1)
-                       copy_len -= end-(va+len);
-
-               memcpy(dst_start,src_start,copy_len);
-               bytes_copied += copy_len;
-       }
-
-       assert(bytes_copied == len);
-
-       return ESUCCESS;
-}
index 4822619..daf0704 100644 (file)
@@ -19,6 +19,7 @@
 #include <process.h>
 #include <schedule.h>
 #include <pmap.h>
+#include <umem.h>
 #include <mm.h>
 #include <trap.h>
 #include <syscall.h>
diff --git a/kern/src/umem.c b/kern/src/umem.c
new file mode 100644 (file)
index 0000000..483d9aa
--- /dev/null
@@ -0,0 +1,324 @@
+/* Copyright (c) 2009, 2010 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * Andrew Waterman <waterman@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Functions for working with userspace's address space.  The user_mem ones need
+ * to involve some form of pinning (TODO), and that global static needs to go. */
+
+#include <ros/common.h>
+#include <umem.h>
+#include <process.h>
+#include <error.h>
+#include <kmalloc.h>
+#include <assert.h>
+#include <pmap.h>
+
+/**
+ * @brief Global variable used to store erroneous virtual addresses as the
+ *        result of a failed user_mem_check().
+ *
+ * zra: What if two checks fail at the same time? Maybe this should be per-cpu?
+ *
+ */
+static void *DANGEROUS RACY user_mem_check_addr;
+
+/**
+ * @brief Check that an environment is allowed to access the range of memory
+ * [va, va+len) with permissions 'perm | PTE_P'.
+ *
+ * Normally 'perm' will contain PTE_U at least, but this is not required.  The
+ * function get_va_perms only checks for PTE_U, PTE_W, and PTE_P.  It won't
+ * check for things like PTE_PS, PTE_A, etc.
+ * 'va' and 'len' need not be page-aligned;
+ *
+ * A user program can access a virtual address if:
+ *     -# the address is below ULIM
+ *     -# the page table gives it permission.  
+ *
+ * If there is an error, 'user_mem_check_addr' is set to the first
+ * erroneous virtual address.
+ *
+ * @param p    the process associated with the user program trying to access
+ *             the virtual address range
+ * @param va   the first virtual address in the range
+ * @param len  the length of the virtual address range
+ * @param perm the permissions the user is trying to access the virtual address 
+ *             range with
+ *
+ * @return VA a pointer of type COUNT(len) to the address range
+ * @return NULL trying to access this range of virtual addresses is not allowed
+ */
+void *user_mem_check(struct proc *p, const void *DANGEROUS va, size_t len,
+                     int perm)
+{
+       if (len == 0) {
+               warn("Called user_mem_check with a len of 0. Don't do that. Returning NULL");
+               return NULL;
+       }
+       
+       // TODO - will need to sort this out wrt page faulting / PTE_P
+       // also could be issues with sleeping and waking up to find pages
+       // are unmapped, though i think the lab ignores this since the 
+       // kernel is uninterruptible
+       void *DANGEROUS start, *DANGEROUS end;
+       size_t num_pages, i;
+       int page_perms = 0;
+
+       perm |= PTE_P;
+       start = ROUNDDOWN((void*DANGEROUS)va, PGSIZE);
+       end = ROUNDUP((void*DANGEROUS)va + len, PGSIZE);
+       if (start >= end) {
+               warn("Blimey!  Wrap around in VM range calculation!");  
+               return NULL;
+       }
+       num_pages = LA2PPN(end - start);
+       for (i = 0; i < num_pages; i++, start += PGSIZE) {
+               page_perms = get_va_perms(p->env_pgdir, start);
+               // ensures the bits we want on are turned on.  if not, error out
+               if ((page_perms & perm) != perm) {
+                       if (i == 0)
+                               user_mem_check_addr = (void*DANGEROUS)va;
+                       else
+                               user_mem_check_addr = start;
+                       return NULL;
+               }
+       }
+       // this should never be needed, since the perms should catch it
+       if ((uintptr_t)end > ULIM) {
+               warn ("I suck - Bug in user permission mappings!");
+               return NULL;
+       }
+       return (void *COUNT(len))TC(va);
+}
+
+/**
+ * @brief Checks that process 'p' is allowed to access the range
+ * of memory [va, va+len) with permissions 'perm | PTE_U'. Destroy 
+ * process 'p' if the assertion fails.
+ *
+ * This function is identical to user_mem_assert() except that it has a side
+ * affect of destroying the process 'p' if the memory check fails.
+ *
+ * @param p    the process associated with the user program trying to access
+ *             the virtual address range
+ * @param va   the first virtual address in the range
+ * @param len  the length of the virtual address range
+ * @param perm the permissions the user is trying to access the virtual address 
+ *             range with
+ *
+ * @return VA a pointer of type COUNT(len) to the address range
+ * @return NULL trying to access this range of virtual addresses is not allowed
+ *              process 'p' is destroyed
+ */
+void *user_mem_assert(struct proc *p, const void *DANGEROUS va, size_t len,
+                       int perm)
+{
+       if (len == 0) {
+               warn("Called user_mem_assert with a len of 0. Don't do that. Returning NULL");
+               return NULL;
+       }
+       
+       void *COUNT(len) res = user_mem_check(p, va, len, perm | PTE_USER_RO);
+       if (!res) {
+               cprintf("[%08x] user_mem_check assertion failure for "
+                       "va %08x\n", p->pid, user_mem_check_addr);
+               proc_destroy(p);        // may not return
+        return NULL;
+       }
+    return res;
+}
+
+/**
+ * @brief Copies data from a user buffer to a kernel buffer.
+ * 
+ * @param p    the process associated with the user program
+ *             from which the buffer is being copied
+ * @param dest the destination address of the kernel buffer
+ * @param va   the address of the userspace buffer from which we are copying
+ * @param len  the length of the userspace buffer
+ *
+ * @return ESUCCESS on success
+ * @return -EFAULT  the page assocaited with 'va' is not present, the user 
+ *                  lacks the proper permissions, or there was an invalid 'va'
+ */
+int memcpy_from_user(struct proc *p, void *dest, const void *DANGEROUS va,
+                     size_t len)
+{
+       const void *DANGEROUS start, *DANGEROUS end;
+       size_t num_pages, i;
+       pte_t *pte;
+       uintptr_t perm = PTE_P | PTE_USER_RO;
+       size_t bytes_copied = 0;
+
+       static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
+
+       start = ROUNDDOWN(va, PGSIZE);
+       end = ROUNDUP(va + len, PGSIZE);
+
+       if (start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
+               return -EFAULT;
+
+       num_pages = LA2PPN(end - start);
+       for (i = 0; i < num_pages; i++) {
+               pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0);
+               if (!pte)
+                       return -EFAULT;
+               if ((*pte & PTE_P) && (*pte & PTE_USER_RO) != PTE_USER_RO)
+                       return -EFAULT;
+               if (!(*pte & PTE_P))
+                       if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_READ))
+                               return -EFAULT;
+
+               void *kpage = KADDR(PTE_ADDR(*pte));
+               const void *src_start = i > 0 ? kpage : kpage + (va - start);
+               void *dst_start = dest + bytes_copied;
+               size_t copy_len = PGSIZE;
+               if (i == 0)
+                       copy_len -= va - start;
+               if (i == num_pages-1)
+                       copy_len -= end - (va + len);
+
+               memcpy(dst_start, src_start, copy_len);
+               bytes_copied += copy_len;
+       }
+       assert(bytes_copied == len);
+       return 0;
+}
+
+/**
+ * @brief Copies data to a user buffer from a kernel buffer.
+ * 
+ * @param p    the process associated with the user program
+ *             to which the buffer is being copied
+ * @param dest the destination address of the user buffer
+ * @param va   the address of the kernel buffer from which we are copying
+ * @param len  the length of the user buffer
+ *
+ * @return ESUCCESS on success
+ * @return -EFAULT  the page assocaited with 'va' is not present, the user 
+ *                  lacks the proper permissions, or there was an invalid 'va'
+ */
+int memcpy_to_user(struct proc *p, void *va, const void *src, size_t len)
+{
+       const void *DANGEROUS start, *DANGEROUS end;
+       size_t num_pages, i;
+       pte_t *pte;
+       uintptr_t perm = PTE_P | PTE_USER_RW;
+       size_t bytes_copied = 0;
+
+       static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
+
+       start = ROUNDDOWN(va, PGSIZE);
+       end = ROUNDUP(va + len, PGSIZE);
+
+       if (start >= (void*SNT)ULIM || end > (void*SNT)ULIM)
+               return -EFAULT;
+
+       num_pages = LA2PPN(end - start);
+       for (i = 0; i < num_pages; i++) {
+               pte = pgdir_walk(p->env_pgdir, start + i * PGSIZE, 0);
+               if (!pte)
+                       return -EFAULT;
+               if ((*pte & PTE_P) && (*pte & PTE_USER_RW) != PTE_USER_RW)
+                       return -EFAULT;
+               if (!(*pte & PTE_P))
+                       if (handle_page_fault(p, (uintptr_t)start + i * PGSIZE, PROT_WRITE))
+                               return -EFAULT;
+               void *kpage = KADDR(PTE_ADDR(*pte));
+               void *dst_start = i > 0 ? kpage : kpage + (va - start);
+               const void *src_start = src + bytes_copied;
+               size_t copy_len = PGSIZE;
+               if (i == 0)
+                       copy_len -= va - start;
+               if (i == num_pages - 1)
+                       copy_len -= end - (va + len);
+               memcpy(dst_start, src_start, copy_len);
+               bytes_copied += copy_len;
+       }
+       assert(bytes_copied == len);
+       return 0;
+}
+
+int memcpy_to_user_errno(struct proc *p, void *dst, const void *src, int len)
+{
+       if (memcpy_to_user(p, dst, src, len)) {
+               set_errno(current_tf, EINVAL);
+               return -1;
+       }
+       return 0;
+}
+
+/* Creates a buffer (kmalloc) and safely copies into it from va.  Can return an
+ * error code.  Check its response with IS_ERR().  Must be paired with
+ * user_memdup_free() if this succeeded. */
+void *user_memdup(struct proc *p, const void *va, int len)
+{
+       void* kva = NULL;
+       if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
+               return ERR_PTR(-ENOMEM);
+       if (memcpy_from_user(p, kva, va, len)) {
+               kfree(kva);
+               return ERR_PTR(-EINVAL);
+       }
+       return kva;
+}
+
+void *user_memdup_errno(struct proc *p, const void *va, int len)
+{
+       void *kva = user_memdup(p, va, len);
+       if (IS_ERR(kva)) {
+               set_errno(current_tf, -PTR_ERR(kva));
+               return NULL;
+       }
+       return kva;
+}
+
+void user_memdup_free(struct proc *p, void *va)
+{
+       kfree(va);
+}
+
+/* Same as memdup, but just does strings.  still needs memdup_freed */
+char *user_strdup(struct proc *p, const char *va0, int max)
+{
+       max++;
+       char* kbuf = (char*)kmalloc(PGSIZE, 0);
+       if (kbuf == NULL)
+               return ERR_PTR(-ENOMEM);
+       int pos = 0, len = 0;
+       const char* va = va0;
+       while (max > 0 && len == 0) {
+               int thislen = MIN(PGSIZE - (uintptr_t)va % PGSIZE, max);
+               if (memcpy_from_user(p, kbuf, va, thislen)) {
+                       kfree(kbuf);
+                       return ERR_PTR(-EINVAL);
+               }
+               const char *nullterm = memchr(kbuf, 0, thislen);
+               if (nullterm)
+                       len = pos + (nullterm - kbuf) + 1;
+               pos += thislen;
+               va += thislen;
+               max -= thislen;
+       }
+       kfree(kbuf);
+       return len ? user_memdup(p, va0, len) : ERR_PTR(-EINVAL);
+}
+
+char *user_strdup_errno(struct proc *p, const char *va, int max)
+{
+       void *kva = user_strdup(p, va, max);
+       if (IS_ERR(kva)) {
+               set_errno(current_tf, -PTR_ERR(kva));
+               return NULL;
+       }
+       return kva;
+}
+
+void *kmalloc_errno(int len)
+{
+       void *kva = NULL;
+       if (len < 0 || (kva = kmalloc(len, 0)) == NULL)
+               set_errno(current_tf, ENOMEM);
+       return kva;
+}
index 3e4c01a..7aab421 100644 (file)
@@ -13,6 +13,7 @@
 #include <kmalloc.h>
 #include <kfs.h>
 #include <pmap.h>
+#include <umem.h>
 
 struct sb_tailq super_blocks = TAILQ_HEAD_INITIALIZER(super_blocks);
 spinlock_t super_blocks_lock = SPINLOCK_INITIALIZER;