hal/{aarch64,arm}: fix possible race condition in hal_cpuReschedule#809
hal/{aarch64,arm}: fix possible race condition in hal_cpuReschedule#809jmaksymowicz wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates the context switching and rescheduling logic across multiple ARM architectures (aarch64, armv7a, armv7r, and armv8r). It defers clearing the spinlock until after the new CPU context has been loaded from the stack, preventing potential race conditions. When a spinlock is provided, the code now calls the non-locked _threads_schedule function, loads the new context pointer, and then releases the spinlock. There are no review comments to address, so no additional feedback is provided.
|
Are you sure this doesn't also affect other targets? From what I can see the implementations are pretty similar, e.g. on phoenix-rtos-kernel/hal/riscv64/_interrupts.S Lines 471 to 494 in 0d8d283 which you deemed the cause of the issue. |
Yes, it seems that On aarch64 and armv7a the spinlock was cleared a lot earlier, even before calling |
agkaminski
left a comment
There was a problem hiding this comment.
That must've been quite a pain to find, good job. One nitpick about assumptions and comments.
| cbz x19, .Lno_spinlock | ||
|
|
||
| /* Call the non-locked version of threads_schedule. | ||
| * TODO: In this and other implementations of hal_cpuReschedule() there's an implicit assumption |
There was a problem hiding this comment.
This assumption is well based - it is there by design. What should be made is to make it clear and explicit to avoid mistakes. Perhaps skip passing spinlock as an argument and pass flag clear/no clear instead?
There was a problem hiding this comment.
For now I think the comment about what spinlock is passed to the hal_cpuReschedule may be duplicated in other archs to avoid confusion why we execute _threads_schedule with some spinlock, when in fact we always do with threads_common.spinlock
There was a problem hiding this comment.
I decided to add a comment to hal/cpu.h explaining how the function should be used - I think that's a good solution for now. Ofc there's possibility of user error but at least the requirement is stated explicitly.
| * * x0 - n (unused) | ||
| * * x1 - pointer to CPU context | ||
| * * x2 - arg (unused)*/ | ||
| bl threads_schedule |
There was a problem hiding this comment.
When we enter hal_cpuReschedule without threads_common.spinlock, after returning from threads_schedule() call the stack we use theoretically could be already freed, similarly to the described riscv64 situation.
This is probably really unlikely, especially since there is few instructions between spinlock clear here, but it could be possible when we consider that threads_schedule can add current thread to threads_common.ghosts (and it really does happen e.g. in process_exception() code path).
We probably could get an interrupt after clearing threads_common spinlock in threads_schedule(?), which would give enough time for the reaper to destroy our stack? (I guess in riscv64 case interrupts are disabled)
Correct me if i'm wrong, this is purely theoretical (meaning: I did not reproduce that)
There was a problem hiding this comment.
I think you're right, in process_exception we need to call hal_cpuReschedule under spinlock - or just call proc_threadEnd instead, as it will handle things properly.
We probably could get an interrupt after clearing threads_common spinlock in threads_schedule(?), which would give enough time for the reaper to destroy our stack? (I guess in riscv64 case interrupts are disabled)
In all implementations of hal_cpuReschedule threads_schedule is called with interrupts disabled and they aren't enabled again until context is restored.
I will open a separate issue for fixing this and the riscv64 potential race conditions.
ziemleszcz
left a comment
There was a problem hiding this comment.
LGTM. Consider updating the comment as @agkaminski suggested.
Also update the PR description to mention that other architectures could theoretically be affected, although the race window so narrow that it's unlikely to occur in practice.
It might also be worth mentioning the similar narrow race windows in interrupt and exception handling that @etiaro pointed out.
764524a to
0b63651
Compare
0b63651 to
39de3d4
Compare
Fix a possible race condition between
hal_cpuRescheduleandproc_reap.Description
A race condition has been identified in
hal_cpuRescheduleimplementation on aarch64 and armv7a that can manifest itself at the end of theexitsyscall.The unwanted interleave starts when
hal_cpuReschedule, is called at the end of handling theexitsyscall on core A. Within the function the spinlock is cleared, then on core B the thread running theproc_reapfunction is woken up.proc_reapthen proceeds to free the resources belonging to the exited thread, including unmapping its kernel stack. However, the kernel stack is still in use by core A. This results in either:The issue is most pronounced on
aarch64a53-zynqmp-qemudue to the fact that it uses spinlocks withwfe/sevand emulation can execute large chunks of code from one CPU before switching to another.On
armv7a-zynq7000-qemuthis may not occur due to the fact that emulation is done on many cores in parallel and spinlocks don't usewfe/sev.On real hardware this may not occur very frequently due to code being executed in parallel, so the first CPU always "wins the race".
The fact that
exitis called frequently within the test suite increases the likelihood of hitting this unwanted interleave - which is why the issue occurs frequently in testing, but seemingly not in practical applications on that platform.The fix from
armv7awas also applied toarmv7randarmv8rfor consistency - even though these platforms don't work in SMP so this race condition will never occur.On
ia32andriscv64platforms the implementation ofhal_cpuRescheduleis different and not susceptible to this error.Motivation and Context
May fix phoenix-rtos/phoenix-rtos-project#1386 and phoenix-rtos/phoenix-rtos-project#1387 - needs more testing
YT: RTOS-1392
Types of changes
How Has This Been Tested?
Checklist:
Special treatment