Fix potential race condition in critical section HAL API

Call underlying HAL implementation to enter critical section/disable interrupts
before incrementing the global critical section counter.

Modify HAL implementations to track first entrances to the critical section and
only update the saved state on first enter.
pull/5346/head
Steven Cartmell 2017-12-19 13:35:43 +00:00
parent a07c07fa63
commit e14bee5209
4 changed files with 42 additions and 10 deletions

View File

@ -28,11 +28,13 @@ static union {
} _state = {0}; } _state = {0};
static bool _use_softdevice_routine = false; static bool _use_softdevice_routine = false;
static bool _state_saved = false;
void hal_critical_section_enter(void) void hal_critical_section_enter(void)
{ {
// Fetch the current state of interrupts // Fetch the current state of interrupts
uint32_t primask = __get_PRIMASK(); uint32_t primask = __get_PRIMASK();
uint8_t temp_state = 0;
// If interrupts are enabled, try to use the soft device // If interrupts are enabled, try to use the soft device
uint8_t sd_enabled; uint8_t sd_enabled;
@ -40,8 +42,12 @@ void hal_critical_section_enter(void)
(sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) && (sd_softdevice_is_enabled(&sd_enabled) == NRF_SUCCESS) &&
(sd_enabled == 1)) { (sd_enabled == 1)) {
// If the softdevice can be used, use it. // If the softdevice can be used, use it.
sd_nvic_critical_region_enter(&_state._sd_state); sd_nvic_critical_region_enter(&temp_state);
_use_softdevice_routine = true; _use_softdevice_routine = true;
if (_state_saved == false) {
_state._sd_state = temp_state;
}
} else { } else {
// If interrupts are enabled, disable them. // If interrupts are enabled, disable them.
if (primask == 0) { if (primask == 0) {
@ -50,13 +56,20 @@ void hal_critical_section_enter(void)
// Store PRIMASK state, it will be restored when exiting critical // Store PRIMASK state, it will be restored when exiting critical
// section. // section.
_state._PRIMASK_state = primask;
_use_softdevice_routine = false; _use_softdevice_routine = false;
if (_state_saved == false) {
_state._PRIMASK_state = primask;
}
} }
_state_saved = true;
} }
void hal_critical_section_exit(void) void hal_critical_section_exit(void)
{ {
_state_saved = false;
// Restore the state as it was prior to entering the critical section. // Restore the state as it was prior to entering the critical section.
if (_use_softdevice_routine) { if (_use_softdevice_routine) {
sd_nvic_critical_region_exit(_state._sd_state) sd_nvic_critical_region_exit(_state._sd_state)

View File

@ -21,6 +21,7 @@
#include <stdbool.h> #include <stdbool.h>
static volatile bool critical_interrupts_enabled = false; static volatile bool critical_interrupts_enabled = false;
static volatile bool state_saved = false;
static bool are_interrupts_enabled(void) static bool are_interrupts_enabled(void)
{ {
@ -34,17 +35,25 @@ static bool are_interrupts_enabled(void)
MBED_WEAK void hal_critical_section_enter(void) MBED_WEAK void hal_critical_section_enter(void)
{ {
critical_interrupts_enabled = are_interrupts_enabled(); const bool interrupt_state = are_interrupts_enabled();
__disable_irq(); __disable_irq();
}
if (state_saved == true) {
return;
}
critical_interrupts_enabled = interrupt_state;
state_saved = true;
}
MBED_WEAK void hal_critical_section_exit() MBED_WEAK void hal_critical_section_exit()
{ {
// Interrupts must be disabled on invoking an exit from a critical section // Interrupts must be disabled on invoking an exit from a critical section
MBED_ASSERT(!are_interrupts_enabled()); MBED_ASSERT(!are_interrupts_enabled());
state_saved = false;
// Restore the IRQs to their state prior to entering the critical section // Restore the IRQs to their state prior to entering the critical section
if (critical_interrupts_enabled == true) { if (critical_interrupts_enabled == true) {
__enable_irq(); __enable_irq();

View File

@ -66,11 +66,9 @@ void core_util_critical_section_enter(void)
MBED_ASSERT(critical_section_reentrancy_counter < UINT32_MAX); MBED_ASSERT(critical_section_reentrancy_counter < UINT32_MAX);
#endif /* FEATURE_UVISOR */ #endif /* FEATURE_UVISOR */
if (critical_section_reentrancy_counter == 0) { hal_critical_section_enter();
hal_critical_section_enter();
}
critical_section_reentrancy_counter++; ++critical_section_reentrancy_counter;
} }
void core_util_critical_section_exit(void) void core_util_critical_section_exit(void)
@ -85,7 +83,7 @@ void core_util_critical_section_exit(void)
return; return;
} }
critical_section_reentrancy_counter--; --critical_section_reentrancy_counter;
if (critical_section_reentrancy_counter == 0) { if (critical_section_reentrancy_counter == 0) {
hal_critical_section_exit(); hal_critical_section_exit();

View File

@ -16,16 +16,28 @@
*/ */
#include "nrf_nvic.h" #include "nrf_nvic.h"
#include <stdbool.h>
#include <stdint.h> // uint32_t, UINT32_MAX #include <stdint.h> // uint32_t, UINT32_MAX
static uint8_t _sd_state = 0; static uint8_t _sd_state = 0;
static volatile bool _state_saved = false;
void hal_critical_section_enter(void) void hal_critical_section_enter(void)
{ {
sd_nvic_critical_region_enter(&_sd_state); uint8_t temp_state = 0;
sd_nvic_critical_region_enter(&temp_state);
if (_state_saved == true) {
return;
}
_sd_state = temp_state;
_state_saved = true;
} }
void hal_critical_section_exit(void) void hal_critical_section_exit(void)
{ {
_state_saved = false;
sd_nvic_critical_region_exit(_sd_state); sd_nvic_critical_region_exit(_sd_state);
} }