From 537d3d8e01eab4706758ab7cde52e500809ae62e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 03:40:54 +0200 Subject: [PATCH 1/2] fix(L9): keep the keypad serviced during import/format blocking loops Co-Authored-By: Claude Opus 4.8 (1M context) --- main.c | 2 +- serial/commands_backup.c | 5 +++++ serial/commands_system.c | 5 +++++ shared/door_verify.h | 7 +++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index 4583119..1b4d84c 100644 --- a/main.c +++ b/main.c @@ -52,7 +52,7 @@ static void boot_network(void) { printf("[main] NTP sync ok\r\n"); } -static void core0_handle_door_verify(void) { +void core0_handle_door_verify(void) { static uint32_t last_handled_seq = 0; uint32_t seq = door_verify_mailbox.request_seq; diff --git a/serial/commands_backup.c b/serial/commands_backup.c index ea18467..a916453 100644 --- a/serial/commands_backup.c +++ b/serial/commands_backup.c @@ -3,6 +3,7 @@ #include "storage/backup.h" #include "shared/wipe.h" #include "libs/base64/base64.h" +#include "shared/door_verify.h" #include "pico/stdlib.h" #include "pico/time.h" #include @@ -83,6 +84,10 @@ void cmd_import_keys(int argc, char **argv) { absolute_time_t deadline = make_timeout_time_ms(60000); // 60s to paste while (!time_reached(deadline)) { + // Core 0 is the only servicer of keypad door-verify requests; pump it + // each iteration so a long paste never starves the keypad (L9). + core0_handle_door_verify(); + int c = getchar_timeout_us(10000); if (c == PICO_ERROR_TIMEOUT) continue; diff --git a/serial/commands_system.c b/serial/commands_system.c index 4b92f9e..717e9ef 100644 --- a/serial/commands_system.c +++ b/serial/commands_system.c @@ -10,6 +10,7 @@ #include "network/ntp.h" #include "storage/backup.h" #include "storage/storage.h" +#include "shared/door_verify.h" #include "shared/totp.h" #include "shared/wipe.h" #include "version.h" @@ -252,6 +253,10 @@ void cmd_format_storage(int argc, char **argv) { absolute_time_t deadline = make_timeout_time_ms(15000); while (!time_reached(deadline) && len < 7) { + // Core 0 is the only servicer of keypad door-verify requests; pump it + // each iteration so the confirm wait never starves the keypad (L9). + core0_handle_door_verify(); + int c = getchar_timeout_us(0); if (c == PICO_ERROR_TIMEOUT) { sleep_ms(10); diff --git a/shared/door_verify.h b/shared/door_verify.h index 86ca83d..ae4fe9c 100644 --- a/shared/door_verify.h +++ b/shared/door_verify.h @@ -35,4 +35,11 @@ typedef struct { extern door_verify_mailbox_t door_verify_mailbox; +// Core-0 servicer of the mailbox: reads a pending keypad request (if any) and +// writes back the verdict. Non-blocking and single-shot per request. Defined in +// main.c and normally called once per core-0 main-loop iteration; also pumped +// inside core-0 blocking loops (import/format waits) so a paste/confirm can +// never starve the keypad (ISSUES.md L9). +void core0_handle_door_verify(void); + #endif From bd67bc7f79681f55049446f3ab4832b02d52952b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 4 Aug 2026 20:26:42 +0200 Subject: [PATCH 2/2] fix(cmd_status): function-scope keys so the coverage/firmware build compiles Rebasing onto current master pulled in a latent build break: cmd_status declares `keys` inside the `if (commands_is_admin())` block but scrubs it via `secure_wipe(keys, sizeof(keys))` at function scope, so `keys` is undeclared there. asan_commands links serial/commands.c (not commands_system.c) so it never compiled this TU, but `make -C test coverage` compiles the whole first-party surface and fails here (`keys undeclared`) -- as does the real firmware build. Restore `keys` to function scope, matching the scrub's intent of always clearing the key DB from BSS (incl. the non-admin path where it stays zero-initialised). Same fix as PR #15 (M1). Co-Authored-By: Claude Opus 4.8 (1M context) --- serial/commands_system.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/serial/commands_system.c b/serial/commands_system.c index 717e9ef..3c4d1e3 100644 --- a/serial/commands_system.c +++ b/serial/commands_system.c @@ -65,11 +65,13 @@ void cmd_status(int argc, char **argv) { printf("ntp: not synced\r\n"); } - // Keys (admin only: key inventory is target-selection data) + // Keys (admin only: key inventory is target-selection data). Declared at + // function scope so the scrub below always runs, even on the non-admin path + // where the array stays zero-initialised. + static key_record_t keys[BACKUP_MAX_KEYS]; if (commands_is_admin()) { - static key_record_t keys[BACKUP_MAX_KEYS]; - int count = storage_key_list(keys, BACKUP_MAX_KEYS); - int enabled = 0, corrupt = 0; + int count = storage_key_list(keys, BACKUP_MAX_KEYS); + int enabled = 0, corrupt = 0; for (int i = 0; i < count; i++) { if (!keys[i].is_checksum_valid) corrupt++;