diff options
author | Jim Mussared <jim.mussared@gmail.com> | 2020-04-03 14:15:18 +1100 |
---|---|---|
committer | Damien George <damien.p.george@gmail.com> | 2020-04-13 21:55:47 +1000 |
commit | 243805d776a19b16545d542b2f51ae88b6e314fe (patch) | |
tree | 939e150defb51389c670ba20585876f32947dca3 /py/vm.c | |
parent | c2cfbcc8d491d225cddbce71de5052ba8048c121 (diff) |
py/scheduler: Fix race in checking scheduler pending state.
Because the atomic section starts after checking whether the scheduler
state is pending, it's possible it can become a different state by the time
the atomic section starts.
This is especially likely on ports where MICROPY_BEGIN_ATOMIC_SECTION is
implemented with a mutex (i.e. it might block), but the race exists
regardless, i.e. if a context switch occurs between those two lines.
Diffstat (limited to 'py/vm.c')
-rw-r--r-- | py/vm.c | 21 |
1 files changed, 13 insertions, 8 deletions
@@ -1366,18 +1366,23 @@ pending_exception_check: #if MICROPY_ENABLE_SCHEDULER // This is an inlined variant of mp_handle_pending if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { - MARK_EXC_IP_SELECTIVE(); mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); - if (obj != MP_OBJ_NULL) { - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; - if (!mp_sched_num_pending()) { - MP_STATE_VM(sched_state) = MP_SCHED_IDLE; + // Re-check state is still pending now that we're in the atomic section. + if (MP_STATE_VM(sched_state) == MP_SCHED_PENDING) { + MARK_EXC_IP_SELECTIVE(); + mp_obj_t obj = MP_STATE_VM(mp_pending_exception); + if (obj != MP_OBJ_NULL) { + MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + if (!mp_sched_num_pending()) { + MP_STATE_VM(sched_state) = MP_SCHED_IDLE; + } + MICROPY_END_ATOMIC_SECTION(atomic_state); + RAISE(obj); } + mp_handle_pending_tail(atomic_state); + } else { MICROPY_END_ATOMIC_SECTION(atomic_state); - RAISE(obj); } - mp_handle_pending_tail(atomic_state); } #else // This is an inlined variant of mp_handle_pending |