diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-12-01 12:45:51 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-12-01 12:47:33 +0100 |
commit | 9ed272ce985fb9af6a5e49f808f66212ef4b767c (patch) | |
tree | 1f5cbf913a745bdee8867ffacadfd6f16472d588 /Kernel | |
parent | f067730f6be9bd10f299d295adfd49175f4e91d7 (diff) | |
download | serenity-9ed272ce985fb9af6a5e49f808f66212ef4b767c.zip |
Kernel: Disable interrupts while setting up a thread blocker
There was a race window between instantiating a WaitQueueBlocker and
setting the thread state to Blocked. If a thread was preempted between
those steps, someone else might try to wake the wait queue and find an
unblocked thread in a wait queue, which is not sane.
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Arch/i386/CPU.h | 15 | ||||
-rw-r--r-- | Kernel/Thread.h | 12 |
2 files changed, 24 insertions, 3 deletions
diff --git a/Kernel/Arch/i386/CPU.h b/Kernel/Arch/i386/CPU.h index 5fa9057bb1..27d7d132f4 100644 --- a/Kernel/Arch/i386/CPU.h +++ b/Kernel/Arch/i386/CPU.h @@ -289,6 +289,21 @@ private: u32 m_flags; }; +inline bool cli_and_save_interrupt_flag() +{ + u32 flags = cpu_flags(); + cli(); + return flags & 0x200; +} + +inline void restore_interrupt_flag(bool flag) +{ + if (flag) + sti(); + else + cli(); +} + class InterruptDisabler { public: InterruptDisabler() diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 6fdcf30b79..0eb939bb89 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -274,11 +274,17 @@ public: ASSERT(state() == Thread::Running); ASSERT(m_blocker == nullptr); - T t(AK::forward<Args>(args)...); + // NOTE: We disable interrupts here to avoid the situation where a WaitQueueBlocker + // adds the current thread to a WaitQueue, and then someone wakes up before + // we set the state to Blocked decides to wake the queue. They would find + // unblocked threads in a wait queue, which would not be good. We can't go + // into Blocked state earlier, since that would prevent this thread from + // getting scheduled. + auto saved_if = cli_and_save_interrupt_flag(); + T t(forward<Args>(args)...); m_blocker = &t; - - // Enter blocked state. set_state(Thread::Blocked); + restore_interrupt_flag(saved_if); // Yield to the scheduler, and wait for us to resume unblocked. if (beneficiary) { |