diff --git a/apps/test-badwrite.c b/apps/test-badwrite.c new file mode 100644 index 00000000..88adf084 --- /dev/null +++ b/apps/test-badwrite.c @@ -0,0 +1,12 @@ +#include +#include +#include + +int main(int argc, char * argv[]) { + int fd = open("/dev/dsp",O_WRONLY); + if (fd < 0) { + fprintf(stderr, "failed to open dsp\n"); + return 1; + } + return write(fd, &fd, -1); +} diff --git a/base/usr/include/kernel/arch/x86_64/mmu.h b/base/usr/include/kernel/arch/x86_64/mmu.h index 2f27a8e2..63cfd402 100644 --- a/base/usr/include/kernel/arch/x86_64/mmu.h +++ b/base/usr/include/kernel/arch/x86_64/mmu.h @@ -12,6 +12,9 @@ #define MMU_GET_MAKE 0x01 +#define MMU_PTR_NULL 1 +#define MMU_PTR_WRITE 2 + void mmu_frame_set(uintptr_t frame_addr); void mmu_frame_clear(uintptr_t frame_addr); int mmu_frame_test(uintptr_t frame_addr); @@ -40,3 +43,5 @@ size_t mmu_total_memory(void); size_t mmu_used_memory(void); void * sbrk(size_t); + +int mmu_validate_user_pointer(void * addr, size_t size, int flags); diff --git a/kernel/arch/x86_64/mmu.c b/kernel/arch/x86_64/mmu.c index 1d017b75..b6027818 100644 --- a/kernel/arch/x86_64/mmu.c +++ b/kernel/arch/x86_64/mmu.c @@ -899,3 +899,26 @@ void * mmu_map_module(size_t size) { } +int mmu_validate_user_pointer(void * addr, size_t size, int flags) { + if (addr == NULL && !(flags & MMU_PTR_NULL)) return 0; + if (size > 0x800000000000) return 0; + + uintptr_t base = (uintptr_t)addr; + uintptr_t end = size ? (base + (size - 1)) : base; + + /* Get start page, end page */ + uintptr_t page_base = base >> 12; + uintptr_t page_end = end >> 12; + + for (uintptr_t page = page_base; page <= page_end; ++page) { + if ((page & 0xffff800000000) != 0 && (page & 0xffff800000000) != 0xffff800000000) return 0; + union PML * page_entry = mmu_get_page_other(this_core->current_process->thread.page_directory->directory, page << 12); + if (!page_entry) return 0; + if (!page_entry->bits.present) return 0; + if (!page_entry->bits.user) return 0; + if (!page_entry->bits.writable && (flags & MMU_PTR_WRITE)) return 0; + } + + return 1; +} + diff --git a/kernel/sys/syscall.c b/kernel/sys/syscall.c index 195be8f7..48a8bf48 100644 --- a/kernel/sys/syscall.c +++ b/kernel/sys/syscall.c @@ -25,14 +25,20 @@ static char hostname[256]; static size_t hostname_len = 0; +extern union PML * mmu_get_page_other(union PML * root, uintptr_t virtAddr); int ptr_validate(void * ptr, const char * syscall) { - if (ptr && !PTR_INRANGE(ptr)) { - send_signal(this_core->current_process->id, SIGSEGV, 1); - return 1; + if (ptr) { + if (!PTR_INRANGE(ptr)) { + send_signal(this_core->current_process->id, SIGSEGV, 1); + return 1; + } + if (!mmu_validate_user_pointer(ptr,1,0)) return 1; } return 0; } +#define PTRCHECK(addr,size,flags) do { if (!mmu_validate_user_pointer(addr,size,flags)) return -EFAULT; } while (0) + static long sys_sbrk(ssize_t size) { if (size & 0xFFF) return -EINVAL; volatile process_t * volatile proc = this_core->current_process; @@ -97,7 +103,7 @@ static long sys_sysfunc(long fn, char ** args) { * the stuff in the middle to be mapped necessarily... */ PTR_VALIDATE(args); if (!args) return -EFAULT; - PTR_VALIDATE(args[0]); + if (!PTR_INRANGE(args[0])) return -EFAULT; if (!args[0]) return -EFAULT; volatile process_t * volatile proc = this_core->current_process; if (proc->group != 0) proc = process_from_pid(proc->group); @@ -119,8 +125,8 @@ static long sys_sysfunc(long fn, char ** args) { /* Align inputs */ uintptr_t start = ((uintptr_t)args[0]) & 0xFFFFffffFFFFf000UL; uintptr_t end = ((uintptr_t)args[0] + (size_t)args[1] + 0xFFF) & 0xFFFFffffFFFFf000UL; - PTR_VALIDATE(start); - PTR_VALIDATE(end); + if (!PTR_INRANGE(start)) return -EFAULT; + if (!PTR_INRANGE(end)) return -EFAULT; for (uintptr_t i = start; i < end; i += 0x1000) { union PML * page = mmu_get_page(i, MMU_GET_MAKE); mmu_frame_allocate(page, MMU_FLAG_WRITABLE); @@ -177,7 +183,7 @@ static long sys_exit(long exitcode) { static long sys_write(int fd, char * ptr, unsigned long len) { if (FD_CHECK(fd)) { - PTR_VALIDATE(ptr); + PTRCHECK(ptr,len,MMU_PTR_NULL); fs_node_t * node = FD_ENTRY(fd); if (!(FD_MODE(fd) & 2)) return -EACCES; if (len && !ptr) { @@ -394,7 +400,7 @@ static long sys_seek(int fd, long offset, long whence) { static long sys_read(int fd, char * ptr, unsigned long len) { if (FD_CHECK(fd)) { - PTR_VALIDATE(ptr); + PTRCHECK(ptr,len,MMU_PTR_NULL|MMU_PTR_WRITE); if (len && !ptr) { return -EFAULT; }