Browse Source

pk: only access user memory through explicit accessors

Enforced with sstatus.SUM.
pull/237/head
Andrew Waterman 5 years ago
parent
commit
817e7d0dfe
  1. 3
      pk/elf.c
  2. 14
      pk/file.c
  3. 3
      pk/mmap.c
  4. 5
      pk/mmap.h
  5. 14
      pk/pk.c
  6. 2
      pk/pk.mk.in
  7. 234
      pk/syscall.c
  8. 66
      pk/usermem.c
  9. 14
      pk/usermem.h

3
pk/elf.c

@ -5,6 +5,7 @@
#include "boot.h"
#include "bits.h"
#include "elf.h"
#include "usermem.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
@ -83,7 +84,7 @@ void load_elf(const char* fn, elf_info* info)
int prot = get_prot(ph[i].p_flags);
if (__do_mmap(vaddr - prepad, ph[i].p_filesz + prepad, prot | PROT_WRITE, flags2, file, ph[i].p_offset - prepad) != vaddr - prepad)
goto fail;
memset((void*)vaddr - prepad, 0, prepad);
memset_user((void*)vaddr - prepad, 0, prepad);
if (!(prot & PROT_WRITE))
if (do_mprotect(vaddr - prepad, ph[i].p_filesz + prepad, prot))
goto fail;

14
pk/file.c

@ -105,7 +105,7 @@ file_t* file_openat(int dirfd, const char* fn, int flags, int mode)
return ERR_PTR(-ENOMEM);
size_t fn_size = strlen(fn)+1;
long ret = frontend_syscall(SYS_openat, dirfd, va2pa(fn), fn_size, flags, mode, 0, 0);
long ret = frontend_syscall(SYS_openat, dirfd, kva2pa(fn), fn_size, flags, mode, 0, 0);
if (ret >= 0)
{
f->kfd = ret;
@ -133,26 +133,22 @@ int fd_close(int fd)
ssize_t file_read(file_t* f, void* buf, size_t size)
{
populate_mapping(buf, size, PROT_WRITE);
return frontend_syscall(SYS_read, f->kfd, va2pa(buf), size, 0, 0, 0, 0);
return frontend_syscall(SYS_read, f->kfd, kva2pa(buf), size, 0, 0, 0, 0);
}
ssize_t file_pread(file_t* f, void* buf, size_t size, off_t offset)
{
populate_mapping(buf, size, PROT_WRITE);
return frontend_syscall(SYS_pread, f->kfd, va2pa(buf), size, offset, 0, 0, 0);
return frontend_syscall(SYS_pread, f->kfd, kva2pa(buf), size, offset, 0, 0, 0);
}
ssize_t file_write(file_t* f, const void* buf, size_t size)
{
populate_mapping(buf, size, PROT_READ);
return frontend_syscall(SYS_write, f->kfd, va2pa(buf), size, 0, 0, 0, 0);
return frontend_syscall(SYS_write, f->kfd, kva2pa(buf), size, 0, 0, 0, 0);
}
ssize_t file_pwrite(file_t* f, const void* buf, size_t size, off_t offset)
{
populate_mapping(buf, size, PROT_READ);
return frontend_syscall(SYS_pwrite, f->kfd, va2pa(buf), size, offset, 0, 0, 0);
return frontend_syscall(SYS_pwrite, f->kfd, kva2pa(buf), size, offset, 0, 0, 0);
}
int file_truncate(file_t* f, off_t len)

3
pk/mmap.c

@ -175,6 +175,7 @@ static int __handle_page_fault(uintptr_t vaddr, int prot)
else if (!(*pte & PTE_V))
{
uintptr_t ppn = vpn + (first_free_paddr / RISCV_PGSIZE);
uintptr_t kva = ppn * RISCV_PGSIZE;
vmr_t* v = (vmr_t*)*pte;
*pte = pte_create(ppn, prot_to_type(PROT_READ|PROT_WRITE, 0));
@ -182,7 +183,7 @@ static int __handle_page_fault(uintptr_t vaddr, int prot)
if (v->file)
{
size_t flen = MIN(RISCV_PGSIZE, v->length - (vaddr - v->addr));
ssize_t ret = file_pread(v->file, (void*)vaddr, flen, vaddr - v->addr + v->offset);
ssize_t ret = file_pread(v->file, (void*)kva, flen, vaddr - v->addr + v->offset);
kassert(ret > 0);
if (ret < RISCV_PGSIZE)
memset((void*)vaddr + ret, 0, RISCV_PGSIZE - ret);

5
pk/mmap.h

@ -34,8 +34,7 @@ uintptr_t do_mremap(uintptr_t addr, size_t old_size, size_t new_size, int flags)
uintptr_t do_mprotect(uintptr_t addr, size_t length, int prot);
uintptr_t do_brk(uintptr_t addr);
#define va2pa(va) ({ uintptr_t __va = (uintptr_t)(va); \
extern uintptr_t first_free_paddr; \
__va >= MEM_START ? __va : __va + first_free_paddr; })
#define kva2pa(va) ((uintptr_t)(va))
#define is_uva(va) ((uintptr_t)(va) < MEM_START)
#endif

14
pk/pk.c

@ -6,6 +6,7 @@
#include "elf.h"
#include "mtrap.h"
#include "frontend.h"
#include "usermem.h"
#include <stdbool.h>
elf_info current;
@ -58,7 +59,7 @@ typedef union {
static size_t parse_args(arg_buf* args)
{
long r = frontend_syscall(SYS_getmainvars, va2pa(args), sizeof(*args), 0, 0, 0, 0, 0);
long r = frontend_syscall(SYS_getmainvars, kva2pa(args), sizeof(*args), 0, 0, 0, 0, 0);
if (r != 0)
panic("args must not exceed %d bytes", (int)sizeof(arg_buf));
@ -86,14 +87,14 @@ static void run_loaded_program(size_t argc, char** argv, uintptr_t kstack_top)
{
// copy phdrs to user stack
size_t stack_top = current.stack_top - current.phdr_size;
memcpy((void*)stack_top, (void*)current.phdr, current.phdr_size);
memcpy_to_user((void*)stack_top, (void*)current.phdr, current.phdr_size);
current.phdr = stack_top;
// copy argv to user stack
for (size_t i = 0; i < argc; i++) {
size_t len = strlen((char*)(uintptr_t)argv[i])+1;
stack_top -= len;
memcpy((void*)stack_top, (void*)(uintptr_t)argv[i], len);
memcpy_to_user((void*)stack_top, (void*)(uintptr_t)argv[i], len);
argv[i] = (void*)stack_top;
}
@ -105,7 +106,7 @@ static void run_loaded_program(size_t argc, char** argv, uintptr_t kstack_top)
for (size_t i = 0; i < envc; i++) {
size_t len = strlen(envp[i]) + 1;
stack_top -= len;
memcpy((void*)stack_top, envp[i], len);
memcpy_to_user((void*)stack_top, envp[i], len);
envp[i] = (void*)stack_top;
}
@ -128,7 +129,8 @@ static void run_loaded_program(size_t argc, char** argv, uintptr_t kstack_top)
// place argc, argv, envp, auxp on stack
#define PUSH_ARG(type, value) do { \
*((type*)sp) = (type)value; \
type __tmp = (type)(value); \
memcpy_to_user(sp, &__tmp, sizeof(type)); \
sp ++; \
} while (0)
@ -187,7 +189,7 @@ void boot_loader(uintptr_t dtb)
write_csr(stvec, &trap_entry);
write_csr(sscratch, 0);
write_csr(sie, 0);
set_csr(sstatus, SSTATUS_SUM | SSTATUS_FS | SSTATUS_VS);
set_csr(sstatus, SSTATUS_FS | SSTATUS_VS);
file_init();
enter_supervisor_mode(rest_of_boot_loader, pk_vm_init(), 0);

2
pk/pk.mk.in

@ -13,6 +13,7 @@ pk_hdrs = \
mmap.h \
pk.h \
syscall.h \
usermem.h \
pk_c_srcs = \
file.c \
@ -22,6 +23,7 @@ pk_c_srcs = \
elf.c \
console.c \
mmap.c \
usermem.c \
pk_asm_srcs = \
entry.S \

234
pk/syscall.c

@ -3,9 +3,11 @@
#include "syscall.h"
#include "pk.h"
#include "file.h"
#include "bits.h"
#include "frontend.h"
#include "mmap.h"
#include "boot.h"
#include "usermem.h"
#include <string.h>
#include <errno.h>
@ -13,6 +15,8 @@ typedef long (*syscall_t)(long, long, long, long, long, long, long);
#define CLOCK_FREQ 1000000000
#define MAX_BUF 512
void sys_exit(int code)
{
if (current.cycle0) {
@ -33,10 +37,25 @@ ssize_t sys_read(int fd, char* buf, size_t n)
{
ssize_t r = -EBADF;
file_t* f = file_get(fd);
char kbuf[MAX_BUF];
if (f) {
for (size_t total = 0; ; ) {
size_t cur = MIN(n - total, MAX_BUF);
r = file_read(f, kbuf, cur);
if (r < 0)
break;
memcpy_to_user(buf, kbuf, r);
total += r;
buf += r;
if (r < cur || total == n) {
r = total;
break;
}
}
if (f)
{
r = file_read(f, buf, n);
file_decref(f);
}
@ -47,10 +66,26 @@ ssize_t sys_pread(int fd, char* buf, size_t n, off_t offset)
{
ssize_t r = -EBADF;
file_t* f = file_get(fd);
char kbuf[MAX_BUF];
if (f) {
for (size_t total = 0; ; ) {
size_t cur = MIN(n - total, MAX_BUF);
r = file_pread(f, kbuf, cur, offset);
if (r < 0)
break;
memcpy_to_user(buf, kbuf, r);
total += r;
buf += r;
offset += r;
if (r < cur || total == n) {
r = total;
break;
}
}
if (f)
{
r = file_pread(f, buf, n, offset);
file_decref(f);
}
@ -61,10 +96,26 @@ ssize_t sys_write(int fd, const char* buf, size_t n)
{
ssize_t r = -EBADF;
file_t* f = file_get(fd);
char kbuf[MAX_BUF];
if (f) {
for (size_t total = 0; ; ) {
size_t cur = MIN(n - total, MAX_BUF);
memcpy_from_user(kbuf, buf, cur);
r = file_write(f, kbuf, cur);
if (r < 0)
break;
total += r;
buf += r;
if (r < cur || total == n) {
r = total;
break;
}
}
if (f)
{
r = file_write(f, buf, n);
file_decref(f);
}
@ -85,7 +136,11 @@ int sys_openat(int dirfd, const char* name, int flags, int mode)
{
int kfd = at_kfd(dirfd);
if (kfd != -1) {
file_t* file = file_openat(kfd, name, flags, mode);
char kname[MAX_BUF];
if (!strcpy_from_user(kname, name, MAX_BUF))
return -ENAMETOOLONG;
file_t* file = file_openat(kfd, kname, flags, mode);
if (IS_ERR_VALUE(file))
return PTR_ERR(file);
@ -114,13 +169,19 @@ int sys_close(int fd)
}
int sys_renameat(int old_fd, const char *old_path, int new_fd, const char *new_path) {
int old_kfd = at_kfd(old_fd);
int new_kfd = at_kfd(new_fd);
if(old_kfd != -1 && new_kfd != -1) {
size_t old_size = strlen(old_path)+1;
size_t new_size = strlen(new_path)+1;
return frontend_syscall(SYS_renameat, old_kfd, va2pa(old_path), old_size,
new_kfd, va2pa(new_path), new_size, 0);
char kold_path[MAX_BUF], knew_path[MAX_BUF];
if (!strcpy_from_user(kold_path, old_path, MAX_BUF) || !strcpy_from_user(knew_path, new_path, MAX_BUF))
return -ENAMETOOLONG;
size_t old_size = strlen(kold_path)+1;
size_t new_size = strlen(knew_path)+1;
return frontend_syscall(SYS_renameat, old_kfd, kva2pa(kold_path), old_size,
new_kfd, kva2pa(knew_path), new_size, 0);
}
return -EBADF;
}
@ -133,8 +194,8 @@ int sys_fstat(int fd, void* st)
if (f)
{
struct frontend_stat buf;
r = frontend_syscall(SYS_fstat, f->kfd, va2pa(&buf), 0, 0, 0, 0, 0);
memcpy(st, &buf, sizeof(buf));
r = frontend_syscall(SYS_fstat, f->kfd, kva2pa(&buf), 0, 0, 0, 0, 0);
memcpy_to_user(st, &buf, sizeof(buf));
file_decref(f);
}
@ -215,8 +276,14 @@ ssize_t sys_lseek(int fd, size_t ptr, int dir)
long sys_lstat(const char* name, void* st)
{
struct frontend_stat buf;
size_t name_size = strlen(name)+1;
long ret = frontend_syscall(SYS_lstat, va2pa(name), name_size, va2pa(&buf), 0, 0, 0, 0);
char kname[MAX_BUF];
if (!strcpy_from_user(kname, name, MAX_BUF))
return -ENAMETOOLONG;
size_t name_size = strlen(kname)+1;
long ret = frontend_syscall(SYS_lstat, kva2pa(kname), name_size, kva2pa(&buf), 0, 0, 0, 0);
memcpy(st, &buf, sizeof(buf));
return ret;
}
@ -226,9 +293,15 @@ long sys_fstatat(int dirfd, const char* name, void* st, int flags)
int kfd = at_kfd(dirfd);
if (kfd != -1) {
struct frontend_stat buf;
size_t name_size = strlen(name)+1;
long ret = frontend_syscall(SYS_fstatat, kfd, va2pa(name), name_size, va2pa(&buf), flags, 0, 0);
memcpy(st, &buf, sizeof(buf));
char kname[MAX_BUF];
if (!strcpy_from_user(kname, name, MAX_BUF))
return -ENAMETOOLONG;
size_t name_size = strlen(kname)+1;
long ret = frontend_syscall(SYS_fstatat, kfd, kva2pa(kname), name_size, kva2pa(&buf), flags, 0, 0);
memcpy_to_user(st, &buf, sizeof(buf));
return ret;
}
return -EBADF;
@ -262,8 +335,13 @@ long sys_faccessat(int dirfd, const char *name, int mode)
{
int kfd = at_kfd(dirfd);
if (kfd != -1) {
size_t name_size = strlen(name)+1;
return frontend_syscall(SYS_faccessat, kfd, va2pa(name), name_size, mode, 0, 0, 0);
char kname[MAX_BUF];
if (!strcpy_from_user(kname, name, MAX_BUF))
return -ENAMETOOLONG;
size_t name_size = strlen(kname)+1;
return frontend_syscall(SYS_faccessat, kfd, kva2pa(kname), name_size, mode, 0, 0, 0);
}
return -EBADF;
}
@ -278,10 +356,16 @@ long sys_linkat(int old_dirfd, const char* old_name, int new_dirfd, const char*
int old_kfd = at_kfd(old_dirfd);
int new_kfd = at_kfd(new_dirfd);
if (old_kfd != -1 && new_kfd != -1) {
size_t old_size = strlen(old_name)+1;
size_t new_size = strlen(new_name)+1;
return frontend_syscall(SYS_linkat, old_kfd, va2pa(old_name), old_size,
new_kfd, va2pa(new_name), new_size,
char kold_name[MAX_BUF], knew_name[MAX_BUF];
if (!strcpy_from_user(kold_name, old_name, MAX_BUF) || !strcpy_from_user(knew_name, new_name, MAX_BUF))
return -ENAMETOOLONG;
size_t old_size = strlen(kold_name)+1;
size_t new_size = strlen(knew_name)+1;
return frontend_syscall(SYS_linkat, old_kfd, kva2pa(kold_name), old_size,
new_kfd, kva2pa(knew_name), new_size,
flags);
}
return -EBADF;
@ -296,8 +380,13 @@ long sys_unlinkat(int dirfd, const char* name, int flags)
{
int kfd = at_kfd(dirfd);
if (kfd != -1) {
size_t name_size = strlen(name)+1;
return frontend_syscall(SYS_unlinkat, kfd, va2pa(name), name_size, flags, 0, 0, 0);
char kname[MAX_BUF];
if (!strcpy_from_user(kname, name, MAX_BUF))
return -ENAMETOOLONG;
size_t name_size = strlen(kname)+1;
return frontend_syscall(SYS_unlinkat, kfd, kva2pa(kname), name_size, flags, 0, 0, 0);
}
return -EBADF;
}
@ -311,8 +400,13 @@ long sys_mkdirat(int dirfd, const char* name, int mode)
{
int kfd = at_kfd(dirfd);
if (kfd != -1) {
size_t name_size = strlen(name)+1;
return frontend_syscall(SYS_mkdirat, kfd, va2pa(name), name_size, mode, 0, 0, 0);
char kname[MAX_BUF];
if (!strcpy_from_user(kname, name, MAX_BUF))
return -ENAMETOOLONG;
size_t name_size = strlen(kname)+1;
return frontend_syscall(SYS_mkdirat, kfd, kva2pa(kname), name_size, mode, 0, 0, 0);
}
return -EBADF;
}
@ -322,10 +416,13 @@ long sys_mkdir(const char* name, int mode)
return sys_mkdirat(AT_FDCWD, name, mode);
}
long sys_getcwd(const char* buf, size_t size)
long sys_getcwd(char* buf, size_t size)
{
populate_mapping(buf, size, PROT_WRITE);
return frontend_syscall(SYS_getcwd, va2pa(buf), size, 0, 0, 0, 0, 0);
char kbuf[MAX_BUF];
long ret = frontend_syscall(SYS_getcwd, kva2pa(kbuf), MIN(size, MAX_BUF), 0, 0, 0, 0, 0);
if (ret == 0)
memcpy_to_user(buf, kbuf, strlen(kbuf) + 1);
return ret;
}
size_t sys_brk(size_t pos)
@ -335,13 +432,19 @@ size_t sys_brk(size_t pos)
int sys_uname(void* buf)
{
const int sz = 65;
strcpy(buf + 0*sz, "Proxy Kernel");
strcpy(buf + 1*sz, "");
strcpy(buf + 2*sz, "4.15.0");
strcpy(buf + 3*sz, "");
strcpy(buf + 4*sz, "");
strcpy(buf + 5*sz, "");
const int sz = 65, sz_total = sz * 6;
char kbuf[sz_total];
memset(kbuf, 0, sz_total);
strcpy(kbuf + 0*sz, "Proxy Kernel");
strcpy(kbuf + 1*sz, "");
strcpy(kbuf + 2*sz, "4.15.0");
strcpy(kbuf + 3*sz, "");
strcpy(kbuf + 4*sz, "");
strcpy(kbuf + 5*sz, "");
memcpy_to_user(buf, kbuf, sz_total);
return 0;
}
@ -382,17 +485,19 @@ uintptr_t sys_mprotect(uintptr_t addr, size_t length, int prot)
int sys_rt_sigaction(int sig, const void* act, void* oact, size_t sssz)
{
if (oact)
memset(oact, 0, sizeof(long) * 3);
if (oact) {
long koact[3] = {0};
memcpy_to_user(oact, koact, sizeof(koact));
}
return 0;
}
long sys_time(long* loc)
{
uint64_t t = rdcycle64() / CLOCK_FREQ;
long t = (long)(rdcycle64() / CLOCK_FREQ);
if (loc)
*loc = t;
memcpy_to_user(loc, &t, sizeof(t));
return t;
}
@ -400,10 +505,11 @@ int sys_times(long* loc)
{
uint64_t t = rdcycle64();
kassert(CLOCK_FREQ % 1000000 == 0);
loc[0] = t / (CLOCK_FREQ / 1000000);
loc[1] = 0;
loc[2] = 0;
loc[3] = 0;
long kloc[4] = {0};
kloc[0] = t / (CLOCK_FREQ / 1000000);
memcpy_to_user(loc, kloc, sizeof(kloc));
return 0;
}
@ -411,8 +517,12 @@ int sys_times(long* loc)
int sys_gettimeofday(long* loc)
{
uint64_t t = rdcycle64();
loc[0] = t / CLOCK_FREQ;
loc[1] = (t % CLOCK_FREQ) / (CLOCK_FREQ / 1000000);
long kloc[2];
kloc[0] = t / CLOCK_FREQ;
kloc[1] = (t % CLOCK_FREQ) / (CLOCK_FREQ / 1000000);
memcpy_to_user(loc, kloc, sizeof(kloc));
return 0;
}
@ -420,8 +530,12 @@ int sys_gettimeofday(long* loc)
long sys_clock_gettime(int clk_id, long *loc)
{
uint64_t t = rdcycle64();
loc[0] = t / CLOCK_FREQ;
loc[1] = (t % CLOCK_FREQ) / (CLOCK_FREQ / 1000000000);
long kloc[2];
kloc[0] = t / CLOCK_FREQ;
kloc[1] = (t % CLOCK_FREQ) / (CLOCK_FREQ / 1000000000);
memcpy_to_user(loc, kloc, sizeof(kloc));
return 0;
}
@ -429,9 +543,11 @@ long sys_clock_gettime(int clk_id, long *loc)
ssize_t sys_writev(int fd, const long* iov, int cnt)
{
ssize_t ret = 0;
for (int i = 0; i < cnt; i++)
{
ssize_t r = sys_write(fd, (void*)iov[2*i], iov[2*i+1]);
for (int i = 0; i < cnt; i++) {
long kiov[2];
memcpy_from_user(kiov, iov + 2*i, 2*sizeof(long));
ssize_t r = sys_write(fd, (void*)kiov[0], kiov[1]);
if (r < 0)
return r;
ret += r;
@ -441,7 +557,11 @@ ssize_t sys_writev(int fd, const long* iov, int cnt)
int sys_chdir(const char *path)
{
return frontend_syscall(SYS_chdir, va2pa(path), 0, 0, 0, 0, 0, 0);
char kbuf[MAX_BUF];
if (!strcpy_from_user(kbuf, path, MAX_BUF))
return -ENAMETOOLONG;
return frontend_syscall(SYS_chdir, kva2pa(kbuf), 0, 0, 0, 0, 0, 0);
}
int sys_getdents(int fd, void* dirbuf, int count)

66
pk/usermem.c

@ -0,0 +1,66 @@
// See LICENSE for license details.
#include "usermem.h"
#include "mmap.h"
#include <string.h>
#include <stdint.h>
void memset_user(void* dst, int ch, size_t n)
{
if ((uintptr_t)dst + n < (uintptr_t)dst || !is_uva(dst + n - 1))
handle_page_fault((uintptr_t)dst, PROT_WRITE);
uintptr_t sstatus = set_csr(sstatus, SSTATUS_SUM);
memset(dst, ch, n);
write_csr(sstatus, sstatus);
}
void memcpy_to_user(void* dst, const void* src, size_t n)
{
if ((uintptr_t)dst + n < (uintptr_t)dst || !is_uva(dst + n - 1))
handle_page_fault((uintptr_t)dst, PROT_WRITE);
uintptr_t sstatus = set_csr(sstatus, SSTATUS_SUM);
memcpy(dst, src, n);
write_csr(sstatus, sstatus);
}
void memcpy_from_user(void* dst, const void* src, size_t n)
{
if ((uintptr_t)src + n < (uintptr_t)src || !is_uva(src + n - 1))
handle_page_fault((uintptr_t)src, PROT_READ);
uintptr_t sstatus = set_csr(sstatus, SSTATUS_SUM);
memcpy(dst, src, n);
write_csr(sstatus, sstatus);
}
bool strcpy_from_user(char* dst, const char* src, size_t n)
{
uintptr_t sstatus = set_csr(sstatus, SSTATUS_SUM);
while (n > 0) {
if (!is_uva(src))
handle_page_fault((uintptr_t)src, PROT_READ);
char ch = *(volatile const char*)src;
*dst = ch;
if (ch == 0)
return true;
src++;
dst++;
n--;
}
write_csr(sstatus, sstatus);
return false;
}

14
pk/usermem.h

@ -0,0 +1,14 @@
// See LICENSE for license details.
#ifndef _PK_USERMEM_H
#define _PK_USERMEM_H
#include <stdbool.h>
#include <stddef.h>
void memset_user(void* dst, int ch, size_t n);
void memcpy_to_user(void* dst, const void* src, size_t n);
void memcpy_from_user(void* dst, const void* src, size_t n);
bool strcpy_from_user(char* dst, const char* src, size_t n);
#endif
Loading…
Cancel
Save