diff --git a/examples/automotive/cheri/send.cc b/examples/automotive/cheri/send.cc index 83da1bb..0d91718 100644 --- a/examples/automotive/cheri/send.cc +++ b/examples/automotive/cheri/send.cc @@ -151,9 +151,35 @@ void reset_error_seen_and_shown() * violation message or not, and display it if so. This will only be * drawn once, and then `errorMessageShown` will be set true. */ -void lcd_display_cheri_message() +void update_cheri_error_handling() { - if (!errorSeen || errorMessageShown) + if (!errorSeen) + { + return; + } + + /* + * We expect that we might correct the compartment's fault many times, + * especially since the error is being triggered every frame. As a + * built-in protection, the CHERIoT RTOS switcher places a default + * limit of 512 times a compartment invocation may fault, to help + * stop compartments getting stuck during recovery. + * + * Since this is a legitimate use case, and we don't want our demo + * to stop working after 512 errors, reset the counter each time. + * + * We have to restore it here and not in the error handler as otherwise + * the switcher will decrement its count and then underflow, which it + * will see as a double fault and then unwind. + * + * See: + * https://github.com/CHERIoT-Platform/cheriot-rtos/issues/299 + * https://github.com/CHERIoT-Platform/cheriot-rtos/blob/9f3731c0e3805ad56a642987be9bc859e2ee1b4e/sdk/include/switcher.h#L40-L41 + */ + switcher_handler_invocation_count_reset(); + errorSeen = false; + + if (errorMessageShown) { return; } @@ -537,7 +563,7 @@ void __cheri_compartment("automotive_send") entry() .wait = wait, .waitTime = 120 * CyclesPerMillisecond, .time = rdcycle64, - .loop = lcd_display_cheri_message, + .loop = update_cheri_error_handling, .start = reset_error_seen_and_shown, .joystick_read = read_joystick, .digital_pedal_read = read_pedal_digital, diff --git a/examples/snake/snake.cc b/examples/snake/snake.cc index 057261b..a9780c2 100644 --- a/examples/snake/snake.cc +++ b/examples/snake/snake.cc @@ -39,6 +39,11 @@ static constexpr Color BackgroundColor = Color::Black, static constexpr Size TileSize = {10, 10}, TileSpacing = {2, 2}, BorderSize = {4, 3}; +// Global flag used by the CHERI compartment error handler to detect when a +// capability violation has occurred, so that we can modify the switcher context +// appropriately. +static bool errorSeen = false; + typedef struct Position { int32_t x; @@ -511,6 +516,26 @@ class SnakeGame return true; } + /* If any fault was seen in the compartment, handle the error.*/ + void handle_compartment_faults() + { + /* + * Just in case somebody wants to play 512+ games of Snake without + * resetting, lets be safe and reset the switcher handler invocation + * count due to our PCC re-installation. + * + * We have to restore it here and not in the error handler as otherwise + * the switcher will decrement its count and then underflow, which it + * will see as a double fault and then unwind. + * + * See: + * https://github.com/CHERIoT-Platform/cheriot-rtos/issues/299 + * https://github.com/CHERIoT-Platform/cheriot-rtos/blob/9f3731c0e3805ad56a642987be9bc859e2ee1b4e/sdk/include/switcher.h#L40-L41 + * */ + switcher_handler_invocation_count_reset(); + errorSeen = false; + } + /** * @brief Runs the main game loop, updating the snake's movement and drawing * new information to the display, and regulates update/frame timing. @@ -551,6 +576,11 @@ class SnakeGame currentTime = rdcycle64(); gameStillActive = update_game_state(gpio, lcd); + + if (errorSeen) + { + handle_compartment_faults(); + } } }; @@ -627,6 +657,7 @@ compartment_error_handler(ErrorState *frame, size_t mcause, size_t mtval) if (exceptionCode == CauseCode::BoundsViolation || exceptionCode == CauseCode::TagViolation) { + errorSeen = true; // If an explicit out of bounds access occurs, or bounds are made // invalid by some negative array access, we **assume** that this was // caused by the SnakeGame::check_if_colliding function and that the