diff --git a/headers/private/kernel/arch/cpu.h b/headers/private/kernel/arch/cpu.h index 188df59081..6f82636016 100644 --- a/headers/private/kernel/arch/cpu.h +++ b/headers/private/kernel/arch/cpu.h @@ -34,13 +34,6 @@ void arch_cpu_invalidate_TLB_list(addr_t pages[], int num_pages); void arch_cpu_user_TLB_invalidate(void); void arch_cpu_global_TLB_invalidate(void); -status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size, - addr_t *faultHandler); -ssize_t arch_cpu_user_strlcpy(char *to, const char *from, size_t size, - addr_t *faultHandler); -status_t arch_cpu_user_memset(void *s, char c, size_t count, - addr_t *faultHandler); - void arch_cpu_sync_icache(void *address, size_t length); diff --git a/headers/private/kernel/arch/generic/user_memory.h b/headers/private/kernel/arch/generic/user_memory.h new file mode 100644 index 0000000000..6b31354ad5 --- /dev/null +++ b/headers/private/kernel/arch/generic/user_memory.h @@ -0,0 +1,86 @@ +/* + * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_GENERIC_USER_MEMORY_H +#define _KERNEL_ARCH_GENERIC_USER_MEMORY_H + + +#include + +#include +#include + +#include + + +namespace { + + +struct FaultHandlerGuard { + FaultHandlerGuard() + { + ASSERT(thread_get_current_thread()->fault_handler == nullptr); + thread_get_current_thread()->fault_handler = HandleFault; + std::atomic_signal_fence(std::memory_order_acq_rel); + } + + + ~FaultHandlerGuard() + { + std::atomic_signal_fence(std::memory_order_acq_rel); + thread_get_current_thread()->fault_handler = nullptr; + } + + + [[noreturn]] static void HandleFault() + { + longjmp(thread_get_current_thread()->fault_handler_state, 1); + } +}; + + +template +bool user_access(Function function) +{ + FaultHandlerGuard guard; + // TODO: try { } catch (...) { } would be much nicer, wouldn't it? + // And faster... And world wouldn't end in a terrible disaster if function() + // or anything it calls created on stack an object with non-trivial + // destructor. + auto fail = setjmp(thread_get_current_thread()->fault_handler_state); + if (fail == 0) { + function(); + return true; + } + dprintf("Hi! This is fault handler speaking.\n"); + return false; +} + + +inline status_t +arch_cpu_user_memcpy(void* src, const void* dst, size_t n) +{ + return user_access([=] { memcpy(src, dst, n); }) ? B_OK : B_ERROR; +} + + +inline status_t +arch_cpu_user_memset(void* src, char v, size_t n) +{ + return user_access([=] { memset(src, v, n); }) ? B_OK : B_ERROR; +} + + +inline ssize_t +arch_cpu_user_strlcpy(char* src, const char* dst, size_t n) +{ + ssize_t result; + return user_access([=, &result] { result = strlcpy(src, dst, n); }) + ? result : B_ERROR; +} + +} + +#endif // _KERNEL_ARCH_GENERIC_USER_MEMORY_H + diff --git a/headers/private/kernel/arch/user_memory.h b/headers/private/kernel/arch/user_memory.h new file mode 100644 index 0000000000..79461e38a0 --- /dev/null +++ b/headers/private/kernel/arch/user_memory.h @@ -0,0 +1,57 @@ +/* + * Copyright 2014, Paweł Dziepak, pdziepak@quarnos.org. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_USER_MEMORY_H +#define _KERNEL_ARCH_USER_MEMORY_H + + +#include + +#include + + +#ifdef __x86_64__ +# include +#else + +extern "C" { + +status_t _arch_cpu_user_memcpy(void* to, const void* from, size_t size, + void (**faultHandler)(void)); +ssize_t _arch_cpu_user_strlcpy(char* to, const char* from, size_t size, + void (**faultHandler)(void)); +status_t _arch_cpu_user_memset(void* s, char c, size_t count, + void (**faultHandler)(void)); + +} + + +static inline status_t +arch_cpu_user_memcpy(void* to, const void* from, size_t size) +{ + return _arch_cpu_user_memcpy(to, from, size, + &thread_get_current_thread()->fault_handler); +} + + +static inline ssize_t +arch_cpu_user_strlcpy(char* to, const char* from, size_t size) +{ + return _arch_cpu_user_strlcpy(to, from, size, + &thread_get_current_thread()->fault_handler); +} + + +static inline status_t +arch_cpu_user_memset(void* s, char c, size_t count) +{ + return _arch_cpu_user_memset(s, c, count, + &thread_get_current_thread()->fault_handler); +} + + +#endif + +#endif // _KERNEL_ARCH_USER_MEMORY_H + diff --git a/headers/private/kernel/thread_types.h b/headers/private/kernel/thread_types.h index bfa1ad1004..a48a48099b 100644 --- a/headers/private/kernel/thread_types.h +++ b/headers/private/kernel/thread_types.h @@ -479,7 +479,8 @@ struct Thread : TeamThreadIteratorEntry, KernelReferenceable { } msg; // write_sem/read_sem are protected by fLock when accessed by // others, the other fields are protected by write_sem/read_sem - addr_t fault_handler; + void (*fault_handler)(void); + jmp_buf fault_handler_state; int32 page_faults_allowed; /* this field may only stay in debug builds in the future */ diff --git a/src/system/kernel/arch/arm/arch_asm.S b/src/system/kernel/arch/arm/arch_asm.S index b5463f8faa..ac82e80602 100644 --- a/src/system/kernel/arch/arm/arch_asm.S +++ b/src/system/kernel/arch/arm/arch_asm.S @@ -105,7 +105,7 @@ FUNCTION_END(arm_get_fp); /* status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandler) */ -FUNCTION(arch_cpu_user_memcpy): +FUNCTION(_arch_cpu_user_memcpy): stmfd sp!, { r4-r6, lr } ldr r6, [r3] @@ -140,10 +140,10 @@ FUNCTION(arch_cpu_user_memcpy): mov r0, #-1 ldmfd sp!, { r4-r6, pc } -FUNCTION_END(arch_cpu_user_memcpy) +FUNCTION_END(_arch_cpu_user_memcpy) /* status_t arch_cpu_user_memset(void *to, char c, size_t count, addr_t *faultHandler) */ -FUNCTION(arch_cpu_user_memset): +FUNCTION(_arch_cpu_user_memset): stmfd sp!, { r4-r5, lr } ldr r5, [r3] @@ -179,10 +179,10 @@ FUNCTION(arch_cpu_user_memset): str r5, [r3] ldmfd sp!, { r4-r5, pc } -FUNCTION_END(arch_cpu_user_memset) +FUNCTION_END(_arch_cpu_user_memset) /* ssize_t arch_cpu_user_strlcpy(void *to, const void *from, size_t size, addr_t *faultHandler) */ -FUNCTION(arch_cpu_user_strlcpy): +FUNCTION(_arch_cpu_user_strlcpy): stmfd sp!, { r4-r6, lr } ldr r5, [r3] ldr r4, =.L_user_strlcpy_error @@ -210,7 +210,7 @@ FUNCTION(arch_cpu_user_strlcpy): str r5, [r3] ldmfd sp!, { r4-r6, pc } -FUNCTION_END(arch_cpu_user_strlcpy) +FUNCTION_END(_arch_cpu_user_strlcpy) /*! \fn void arch_debug_call_with_fault_handler(cpu_ent* cpu, diff --git a/src/system/kernel/arch/arm/arch_int.cpp b/src/system/kernel/arch/arm/arch_int.cpp index dcc0973564..1de4426c2e 100644 --- a/src/system/kernel/arch/arm/arch_int.cpp +++ b/src/system/kernel/arch/arm/arch_int.cpp @@ -270,7 +270,7 @@ arch_arm_data_abort(struct iframe *frame) "debugger!\n"); debug_set_page_fault_info(far, frame->pc, isWrite ? DEBUG_PAGE_FAULT_WRITE : 0); - frame->pc = thread->fault_handler; + frame->pc = reinterpret_cast(thread->fault_handler); return; } } @@ -286,9 +286,10 @@ arch_arm_data_abort(struct iframe *frame) // TODO: Now we are generally allowing user_memcpy() with interrupts // disabled, which in most cases is a bug. We should add some thread // flag allowing to explicitly indicate that this handling is desired. + uintptr_t handler = reinterpret_cast(thread->fault_handler); if (thread && thread->fault_handler != 0) { - if (frame->pc != thread->fault_handler) { - frame->pc = thread->fault_handler; + if (frame->pc != handler) { + frame->pc = handler; return; } diff --git a/src/system/kernel/arch/x86/32/arch.S b/src/system/kernel/arch/x86/32/arch.S index 68d7038be8..d796fa6e91 100644 --- a/src/system/kernel/arch/x86/32/arch.S +++ b/src/system/kernel/arch/x86/32/arch.S @@ -142,7 +142,7 @@ FUNCTION_END(x86_reboot) /* status_t arch_cpu_user_memcpy(void *to, const void *from, size_t size, addr_t *faultHandler) */ -FUNCTION(arch_cpu_user_memcpy): +FUNCTION(_arch_cpu_user_memcpy): pushl %esi pushl %edi movl 12(%esp),%edi /* dest */ @@ -182,11 +182,11 @@ FUNCTION(arch_cpu_user_memcpy): popl %edi popl %esi ret -FUNCTION_END(arch_cpu_user_memcpy) +FUNCTION_END(_arch_cpu_user_memcpy) /* status_t arch_cpu_user_memset(void *to, char c, size_t count, addr_t *faultHandler) */ -FUNCTION(arch_cpu_user_memset): +FUNCTION(_arch_cpu_user_memset): pushl %esi pushl %edi movl 12(%esp),%edi /* dest */ @@ -217,11 +217,11 @@ FUNCTION(arch_cpu_user_memset): popl %edi popl %esi ret -FUNCTION_END(arch_cpu_user_memset) +FUNCTION_END(_arch_cpu_user_memset) /* ssize_t arch_cpu_user_strlcpy(void *to, const void *from, size_t size, addr_t *faultHandler) */ -FUNCTION(arch_cpu_user_strlcpy): +FUNCTION(_arch_cpu_user_strlcpy): pushl %esi pushl %edi pushl %ebx @@ -292,7 +292,7 @@ FUNCTION(arch_cpu_user_strlcpy): popl %edi popl %esi ret -FUNCTION_END(arch_cpu_user_strlcpy) +FUNCTION_END(_arch_cpu_user_strlcpy) /*! \fn void arch_debug_call_with_fault_handler(cpu_ent* cpu, diff --git a/src/system/kernel/arch/x86/64/arch.S b/src/system/kernel/arch/x86/64/arch.S index 19c74b4d2d..6fbc3d8603 100644 --- a/src/system/kernel/arch/x86/64/arch.S +++ b/src/system/kernel/arch/x86/64/arch.S @@ -63,142 +63,6 @@ done: FUNCTION_END(x86_reboot) -/* status_t arch_cpu_user_memcpy(void* to, const void* from, size_t size, - addr_t* faultHandler) */ -FUNCTION(arch_cpu_user_memcpy): - // faultHandler -> r8, size -> rcx. - movq %rcx, %r8 - movq %rdx, %rcx - - // Set the fault handler, preserve old in rax. - movq (%r8), %rax - movq $.L_user_memcpy_error, (%r8) - - // Move by quadwords. - cld - movq %rcx, %r9 - shrq $3, %rcx - rep - movsq - - // Move any remaining data by bytes. - movq %r9, %rcx - andq $7, %rcx - rep - movsb - - // Restore the old fault handler and return. - movq %rax, (%r8) - xorl %eax, %eax - ret - -.L_user_memcpy_error: - // Restore the old fault handler. Return a generic error, the wrapper - // routine will deal with it. - movq %rax, (%r8) - movl $-1, %eax - ret -FUNCTION_END(arch_cpu_user_memcpy) - - -/* status_t arch_cpu_user_memset(void* to, char c, size_t count, - addr_t* faultHandler) */ -FUNCTION(arch_cpu_user_memset): - // c -> al, faultHandler -> r8, size -> rcx. - movw %si, %ax - movq %rcx, %r8 - movq %rdx, %rcx - - // Set the fault handler, preserve old in rdx. - movq (%r8), %rdx - movq $.L_user_memset_error, (%r8) - - rep - stosb - - // Restore the old fault handler and return. - movq %rdx, (%r8) - xorl %eax, %eax - ret - -.L_user_memset_error: - // Restore the old fault handler. Return a generic error, the wrapper - // routine will deal with it. - movq %rdx, (%r8) - movl $-1, %eax - ret -FUNCTION_END(arch_cpu_user_memset) - - -/* ssize_t arch_cpu_user_strlcpy(void* to, const void* from, size_t size, - addr_t* faultHandler) */ -FUNCTION(arch_cpu_user_strlcpy): - // faultHandler -> r8, size -> rcx, source -> r9 (original value needed to - // calculate return value). - movq %rcx, %r8 - movq %rdx, %rcx - movq %rsi, %r9 - - // Set the fault handler, preserve old in rax. - movq (%r8), %rax - movq $.L_user_strlcpy_error, (%r8) - - // Check for 0 length. - cmp $0, %rcx - je .L_user_strlcpy_source_count - - // Copy at most count - 1 bytes. - dec %rcx - - // If count is now 0, skip straight to null terminating as our loop will - // otherwise overflow. - jnz .L_user_strlcpy_copy_begin - movb $0, (%rdi) - jmp .L_user_strlcpy_source_count - -.L_user_strlcpy_copy_begin: - cld -.L_user_strlcpy_copy_loop: - // Move data by bytes. - lodsb - stosb - test %al, %al - jz .L_user_strlcpy_source_done - loop .L_user_strlcpy_copy_loop - - // Null terminate string. - movb $0, (%rdi) - dec %rsi - -.L_user_strlcpy_source_count: - // Count remaining bytes in src - not %rcx - # %rcx was 0 and is now max - xor %al, %al - movq %rsi, %rdi - repnz - scasb - movq %rdi, %rsi - -.L_user_strlcpy_source_done: - // Restore the old fault handler - movq %rax, (%r8) - - // Calculate total string length and return. - movq %rsi, %rax - subq %r9, %rax - dec %rax - ret - -.L_user_strlcpy_error: - // Restore the old fault handler. Return a generic error, the wrapper - // routine will deal with it. - movq %rax, (%r8) - movq $-1, %rax - ret -FUNCTION_END(arch_cpu_user_strlcpy) - - /*! \fn void arch_debug_call_with_fault_handler(cpu_ent* cpu, jmp_buf jumpBuffer, void (*function)(void*), void* parameter) diff --git a/src/system/kernel/arch/x86/arch_int.cpp b/src/system/kernel/arch/x86/arch_int.cpp index 89b82575df..ea5ada6d3c 100644 --- a/src/system/kernel/arch/x86/arch_int.cpp +++ b/src/system/kernel/arch/x86/arch_int.cpp @@ -279,7 +279,7 @@ x86_page_fault_exception(struct iframe* frame) debug_set_page_fault_info(cr2, frame->ip, (frame->error_code & 0x2) != 0 ? DEBUG_PAGE_FAULT_WRITE : 0); - frame->ip = thread->fault_handler; + frame->ip = reinterpret_cast(thread->fault_handler); return; } } @@ -295,9 +295,10 @@ x86_page_fault_exception(struct iframe* frame) // TODO: Now we are generally allowing user_memcpy() with interrupts // disabled, which in most cases is a bug. We should add some thread // flag allowing to explicitly indicate that this handling is desired. + uintptr_t handler = reinterpret_cast(thread->fault_handler); if (thread && thread->fault_handler != 0) { - if (frame->ip != thread->fault_handler) { - frame->ip = thread->fault_handler; + if (frame->ip != handler) { + frame->ip = handler; return; } diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index 0f705ceb26..991b9f8f30 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -4219,7 +4220,7 @@ vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isExecute, // this will cause the arch dependant page fault handler to // modify the IP on the interrupt frame or whatever to return // to this address - *newIP = thread->fault_handler; + *newIP = reinterpret_cast(thread->fault_handler); } else { // unhandled page fault in the kernel panic("vm_page_fault: unhandled page fault in kernel space at " @@ -5255,8 +5256,7 @@ user_memcpy(void* to, const void* from, size_t size) if ((addr_t)from + size < (addr_t)from || (addr_t)to + size < (addr_t)to) return B_BAD_ADDRESS; - if (arch_cpu_user_memcpy(to, from, size, - &thread_get_current_thread()->fault_handler) < B_OK) + if (arch_cpu_user_memcpy(to, from, size) < B_OK) return B_BAD_ADDRESS; return B_OK; @@ -5286,8 +5286,7 @@ user_strlcpy(char* to, const char* from, size_t size) // NOTE: Since arch_cpu_user_strlcpy() determines the length of \a from, // the source address might still overflow. - ssize_t result = arch_cpu_user_strlcpy(to, from, maxSize, - &thread_get_current_thread()->fault_handler); + ssize_t result = arch_cpu_user_strlcpy(to, from, maxSize); // If we hit the address overflow boundary, fail. if (result < 0 || (result >= 0 && (size_t)result >= maxSize @@ -5305,9 +5304,7 @@ user_memset(void* s, char c, size_t count) // don't allow address overflows if ((addr_t)s + count < (addr_t)s) return B_BAD_ADDRESS; - - if (arch_cpu_user_memset(s, c, count, - &thread_get_current_thread()->fault_handler) < B_OK) + if (arch_cpu_user_memset(s, c, count) < B_OK) return B_BAD_ADDRESS; return B_OK;