PeachOS
PeachOS copied to clipboard
Wrong EFLAGS are restored when kernel is switching back to user mode
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.
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
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.
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