// User-memory access helpers — see include/uaccess.h for the rules. // // Everything here validates against the *current* process's page table // (myproc()->pgdir), which is also the live CR3 while a syscall runs, so a // range that passes access_ok() can be dereferenced directly by kernel // code for the remainder of the syscall (single-threaded processes can't // unmap it out from under us mid-call). #include "types.h" #include "defs.h" #include "param.h" #include "memlayout.h" #include "mmu.h" #include "proc.h" #include "errno.h" #include "uaccess.h" int access_ok(addr_t uaddr, uint64 size) { addr_t a, end; if(size == 0) return 1; end = uaddr + size; if(end < uaddr || end > KERNBASE) return 0; for(a = PGROUNDDOWN(uaddr); a < end; a += PGSIZE) if(uva2ka(myproc()->pgdir, (char*)a) == 0) return 0; return 1; } void* user_ptr(addr_t uaddr, uint64 size) { if(uaddr == 0 || !access_ok(uaddr, size)) return 0; return (void*)uaddr; } int copy_from_user(void *dst, addr_t uaddr, uint64 n) { if(uaddr + n < uaddr || uaddr + n > KERNBASE) return -EFAULT; if(copyin(myproc()->pgdir, uaddr, dst, n) < 0) return -EFAULT; return 0; } int copy_to_user(addr_t uaddr, const void *src, uint64 n) { if(uaddr + n < uaddr || uaddr + n > KERNBASE) return -EFAULT; if(copyout(myproc()->pgdir, uaddr, (void*)src, n) < 0) return -EFAULT; return 0; } long strlen_user(addr_t uaddr) { addr_t a = uaddr; while(a - uaddr < USER_STR_MAX){ // Scan through the per-page kernel alias so an unmapped page is a // clean failure instead of a fault. char *ka = uva2ka(myproc()->pgdir, (char*)a); if(ka == 0) return -EFAULT; addr_t pgend = PGROUNDDOWN(a) + PGSIZE; for(; a < pgend; a++, ka++) if(*ka == 0) return a - uaddr; } return -EFAULT; }