diff --git a/src/system/kernel/arch/x86/arch_interrupts.S b/src/system/kernel/arch/x86/arch_interrupts.S index d63ecbbb52..5842b603c6 100644 --- a/src/system/kernel/arch/x86/arch_interrupts.S +++ b/src/system/kernel/arch/x86/arch_interrupts.S @@ -53,7 +53,7 @@ call system_time; \ \ movl %eax, %ebx; /* save for later */ \ - movl %edx, %ecx; + movl %edx, %ecx; \ \ /* thread->kernel_time += now - thread->last_time; */ \ sub THREAD_last_time(%edi), %eax; \ @@ -248,7 +248,16 @@ int_bottom: testl $0x20000, IFRAME_flags(%ebp) // VM86 mode jnz int_bottom_vm86 - cmp $USER_CODE_SEG, IFRAME_cs(%ebp) + cmp $USER_CODE_SEG, IFRAME_cs(%ebp) // user mode + je int_bottom_user + + // We need to recheck user mode using the thread's in_kernel flag, since + // sysexit introduces a raced condition: It doesn't reenable interrupts, + // so that we have to do it in the instruction before, thus opening a + // window for an interrupt while still being in the kernel, but having set + // up everything for userland already. + movl %dr3, %edi // thread pointer + cmpb $0, THREAD_in_kernel(%edi) je int_bottom_user // disable interrupts -- the handler will enable them, if necessary @@ -474,8 +483,7 @@ handle_syscall: // make sure interrupts are enabled (they are, when coming from a syscall // but otherwise they might be disabled) sti - call thread_at_kernel_exit - cli // disable interrupts + call thread_at_kernel_exit // also disables interrupts jmp kernel_exit_work_done bad_syscall_params: diff --git a/src/system/kernel/arch/x86/arch_thread.cpp b/src/system/kernel/arch/x86/arch_thread.cpp index 7e528acd5a..40ded08709 100644 --- a/src/system/kernel/arch/x86/arch_thread.cpp +++ b/src/system/kernel/arch/x86/arch_thread.cpp @@ -409,7 +409,8 @@ arch_thread_enter_userspace(struct thread *t, addr_t entry, void *args1, if (user_memcpy((void *)stackTop, args, sizeof(args)) < B_OK) return B_BAD_ADDRESS; - disable_interrupts(); + thread_at_kernel_exit(); + // also disables interrupts i386_set_tss_and_kstack(t->kernel_stack_top); diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index 4e1cb2195e..67e95a350c 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -331,9 +331,6 @@ _create_user_thread_kentry(void) { struct thread *thread = thread_get_current_thread(); - // a signal may have been delivered here - thread_at_kernel_exit(); - // jump to the entry point in user space arch_thread_enter_userspace(thread, (addr_t)thread->entry, thread->args1, thread->args2); @@ -1571,6 +1568,8 @@ thread_at_kernel_entry(bigtime_t now) /*! Called whenever a thread exits kernel space to user space. Tracks time, handles signals, ... + Interrupts must be enabled. When the function returns, interrupts will be + disabled. */ void thread_at_kernel_exit(void) @@ -1584,7 +1583,7 @@ thread_at_kernel_exit(void) scheduler_reschedule(); } - cpu_status state = disable_interrupts(); + disable_interrupts(); thread->in_kernel = false; @@ -1592,14 +1591,12 @@ thread_at_kernel_exit(void) bigtime_t now = system_time(); thread->kernel_time += now - thread->last_time; thread->last_time = now; - - restore_interrupts(state); } /*! The quick version of thread_kernel_exit(), in case no signals are pending and no debugging shall be done. - Interrupts are disabled in this case. + Interrupts must be disabled. */ void thread_at_kernel_exit_no_signals(void)