From b63a9f5ba94668be4391a67a9ba45b4e3f8c1b5c Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 5 Nov 2022 11:54:55 +0100 Subject: [PATCH] Kernel: Improve strdup_from_user and add copy to and from user Still a bit funky, that's why we're not using it in read() and write() yet. --- kernel/include/sys/UserMemory.h | 3 + kernel/src/sys/UserMemory.cpp | 156 +++++++++++++++++++++++++++++++- 2 files changed, 154 insertions(+), 5 deletions(-) diff --git a/kernel/include/sys/UserMemory.h b/kernel/include/sys/UserMemory.h index 3ce99d3f..c2c9936e 100644 --- a/kernel/include/sys/UserMemory.h +++ b/kernel/include/sys/UserMemory.h @@ -16,6 +16,9 @@ bool validate_user_writable_page(uintptr_t address); bool validate_user_read(uintptr_t address, size_t size); bool validate_user_write(uintptr_t address, size_t size); +bool copy_from_user(const void* user_ptr, void* ptr, size_t size); +bool copy_to_user(void* user_ptr, const void* ptr, size_t size); + // FIXME: Map the physical addresses into kernel address space. Right now, something overwrites KernelHeap and crashes // it, so that's not really possible. But it should be done in the future. diff --git a/kernel/src/sys/UserMemory.cpp b/kernel/src/sys/UserMemory.cpp index c2b7b9a5..218d0a8d 100644 --- a/kernel/src/sys/UserMemory.cpp +++ b/kernel/src/sys/UserMemory.cpp @@ -1,14 +1,44 @@ #include "sys/UserMemory.h" #include "memory/Memory.h" +#include "memory/MemoryManager.h" +#include "std/stdlib.h" #include "std/string.h" #include "utils/Addresses.h" -char* strdup_from_user( - const char* user_string) // FIXME: This function is a little hacky. Use the obtain_user_ref and similar functions. +struct dynamic_string { - uint64_t phys = VMM::get_physical((uint64_t)user_string); - if (phys == (uint64_t)-1) { return nullptr; } - return strdup((const char*)phys); + char* buf; + long capacity; + long size; +}; + +bool dynamic_expand(dynamic_string* str, long new_capacity) +{ + char* buffer = (char*)krealloc(str->buf, new_capacity); + if (!buffer) { return false; } + str->buf = buffer; + str->capacity = new_capacity; + return true; +} + +bool dynamic_push(dynamic_string* str, char c) +{ + if (str->size == str->capacity) + { + if (!dynamic_expand(str, str->capacity + 16)) return false; + } + str->buf[str->size] = c; + str->size++; + return true; +} + +bool dynamic_init(dynamic_string* str) +{ + str->buf = (char*)kmalloc(10); + if (!str->buf) return false; + str->capacity = 10; + str->size = 0; + return true; } bool validate_user_readable_page(uintptr_t address) @@ -29,6 +59,54 @@ bool validate_user_writable_page(uintptr_t address) return false; } +char* strdup_from_user(const char* user_string) +{ + uintptr_t user_ptr = (uintptr_t)user_string; + auto aligned = round_down_to_nearest_page(user_ptr); + char* ptr = nullptr; + uintptr_t index = 0; + if (aligned != user_ptr) // Otherwise, we already do this check below. + { + if (!validate_user_readable_page(aligned)) return nullptr; + ptr = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(aligned), 0); + index = user_ptr - aligned; + } + dynamic_string str; + if (!dynamic_init(&str)) + { + if (ptr) MemoryManager::release_mapping(ptr); + return nullptr; + } + while (true) // FIXME: set a limit for this and fail with ENAMETOOLONG otherwise. + { + if (user_ptr % PAGE_SIZE == 0) + { + index = 0; + if (ptr) MemoryManager::release_mapping(ptr); + if (!validate_user_readable_page(user_ptr)) + { + kfree(str.buf); + return nullptr; + } + ptr = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(user_ptr), 0); + } + char c = ptr[index]; + if (!dynamic_push(&str, c)) + { + MemoryManager::release_mapping(ptr); + kfree(str.buf); + return nullptr; + } + if (!c) // We reached the null terminator!! + { + MemoryManager::release_mapping(ptr); + return str.buf; + } + user_ptr++; + index++; + } +} + bool validate_user_read(uintptr_t address, size_t size) { auto aligned = round_down_to_nearest_page(address); @@ -63,4 +141,72 @@ bool validate_user_write(uintptr_t address, size_t size) address++; } return true; +} + +bool do_copy_from_user(const char* uptr, char* ptr, size_t size) +{ + uintptr_t user_ptr = (uintptr_t)uptr; + auto aligned = round_down_to_nearest_page(user_ptr); + char* mapping = nullptr; + uintptr_t index = 0; + if (aligned != user_ptr) // Otherwise, we already do this check below. + { + if (!validate_user_readable_page(aligned)) return false; + mapping = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(aligned), 0); + index = user_ptr - aligned; + } + while (size--) + { + if (user_ptr % PAGE_SIZE == 0) + { + if (mapping) MemoryManager::release_mapping(mapping); + index = 0; + if (!validate_user_readable_page(user_ptr)) return false; + mapping = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(user_ptr), 0); + } + *ptr = mapping[index]; + user_ptr++; + ptr++; + index++; + } + return true; +} + +bool do_copy_to_user(char* uptr, const char* ptr, size_t size) +{ + uintptr_t user_ptr = (uintptr_t)uptr; + auto aligned = round_down_to_nearest_page(user_ptr); + char* mapping = nullptr; + uintptr_t index = 0; + if (aligned != user_ptr) // Otherwise, we already do this check below. + { + if (!validate_user_writable_page(aligned)) return false; + mapping = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(aligned)); + index = user_ptr - aligned; + } + while (size--) + { + if (user_ptr % PAGE_SIZE == 0) + { + if (mapping) MemoryManager::release_mapping(mapping); + index = 0; + if (!validate_user_writable_page(user_ptr)) return false; + mapping = (char*)MemoryManager::get_mapping((void*)VMM::get_physical(user_ptr)); + } + mapping[index] = *ptr; + user_ptr++; + ptr++; + index++; + } + return true; +} + +bool copy_from_user(const void* user_ptr, void* ptr, size_t size) +{ + return do_copy_from_user((const char*)user_ptr, (char*)ptr, size); +} + +bool copy_to_user(void* user_ptr, const void* ptr, size_t size) +{ + return do_copy_to_user((char*)user_ptr, (const char*)ptr, size); } \ No newline at end of file