nibblebits / PeachOS

Simple kernel designed for a online course
GNU General Public License v2.0
133 stars 55 forks source link

Wrong EFLAGS are restored when kernel is switching back to user mode #9

Open roginvs opened 1 year ago

roginvs commented 1 year ago

When we call task_return function we for some reason push FLAGS which are from kernel code but not from the userspace code. This causes userspace code to have wrong FLAGS after context switching.

Here is a simple proof-of-concept for user space program blank.c (https://github.com/roginvs/udemy_kernel/commit/a384f9747a060ca53329fd5111612ba590abc314):

    mov eax, 0
    cmp eax, 0
    .loop
    nop
    jz .loop
    nop
    ret

This looks like infinite loop. But when timer interrupt occurs and control is transferred back to userspace code then EFLAGS have ZF=0 and it causes this loop to break.

A solution is here https://github.com/nibblebits/PeachOS/pull/6/files - we have to push to stack EFLAGS the same way as other registers and also we have to provide initialization value for EFLAGS for userspace code.

nibblebits commented 1 year ago

Hi, Can you explain what you believe to be wrong with the current solution below:

    ; Push the flags
    pushf
    pop eax
    or eax, 0x200
    push eax

Once I can confirm if your mistaken or if this is a bug I can take further action Thanks

roginvs commented 1 year ago

Let's start with proof-of-concept code for userspace program (https://github.com/roginvs/udemy_kernel/commit/a384f9747a060ca53329fd5111612ba590abc314). I will add some comments there:


mov eax, 0
cmp eax, 0 ; This raises ZF from EFLAGS register
.loop ; We should jump to this label infinitely because ZF flag is set
nop ; Assume at some point we have timer interruption here
jz .loop
ret

When timer interruption occurs then CPU saves registers in the kernel stack. When kernel is ready to return control back to this program then we need to restore exact the same state as it was before.

In the code below we still running in the kernel mode and EFLAGS have flags which are valid for kernel code. At this point ZF might be not set, it fully depends of the kernel and what we compared few instructions before in the kernel mode. In this section

    ; Push the flags
    pushf
    pop eax
    or eax, 0x200
    push eax

we set push EFLAGS from kernel mode and ZF might be zero.

Proof-of-concept really shows this bug. If you run this user program then you will see that at some point it will break from infinite loop which is obviously not the expected behaviour.

nibblebits commented 1 year ago

Woopsie I see what you mean now. Quite right, because here we push the flags as they are at kernel land at that point in time, meaning the flags might not be the same when returning to the user process if theirs a task switch.

Thats what your getting at right