Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
roginvs opened this issue Nov 18, 2022 · 3 comments
Open

Comments

@roginvs
Copy link

roginvs commented Nov 18, 2022

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 (roginvs/udemy_kernel@a384f97):

    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
Copy link
Owner

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
Copy link
Author

roginvs commented Dec 10, 2022

Let's start with proof-of-concept code for userspace program (roginvs/udemy_kernel@a384f97). 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
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants