feat(core): Add error handling in driver init functions + update syscall stubs.
What changed, and why it matters
This commit changes how the Trezor firmware's vibration motor (haptic) driver reports and handles errors. Previously, some haptic functions returned simple true/false success values, and some callers ignored the result of initialization. Now those functions return a structured status code, callers capture the status (though mostly still ignore it), and one resume path actually halts on failure. A test-only haptic syscall was removed. The changes are defensive cleanups rather than a fix for a known exploitable bug, but they reduce the chance that a failing haptic device could leave the system in an inconsistent state.
Treat as a routine hardening commit. Review whether the remaining UNUSED(status) patterns in bootloader/kernel/prodtest should be replaced with ensure_ok or explicit error handling, since silently ignoring haptic init failures could mask hardware or tampering issues. Verify that removing SYSCALL_HAPTIC_TEST does not break any production or test workflows.
Security signals we found
Driver initialization errors were previously silently ignored in bootloader, kernel, and prodtest
Return type change from bool to ts_t improves error propagation across the syscall boundary
Removal of SYSCALL_HAPTIC_TEST reduces attack surface by eliminating a test-only syscall
resume_drivers now halts on haptic initialization failure via ensure_ok
Added cleanup logging on haptic_init failure
Evidence from the diff
The patch updates the haptic driver API to propagate ts_t status codes instead of bool return values for haptic_set_enabled, haptic_play, and haptic_play_custom. Syscall dispatch and stubs are updated to encode/decode ts_code/ts_make. SYSCALL_HAPTIC_TEST is removed. haptic_init’s error path now logs a warning and cleans up. Bootloader, kernel, and prodtest now capture the init status but mark it UNUSED, so they still do not act on failure. The suspend/resume path (resume_drivers) is the only caller that now explicitly ensures_ok(status, …) and will halt if haptic resume fails. syslog_config.h gains a haptic_driver log level define.
Changed components
core/embed/io/haptic/drv262x/drv262x.ccore/embed/projects/bootloader/main.ccore/embed/projects/kernel/main.ccore/embed/projects/prodtest/main.ccore/embed/sys/dbg/inc/sys/syslog_config.hcore/embed/sys/suspend/stm32u5/suspend_io.ccore/embed/sys/syscall/inc/sys/syscall_numbers.hcore/embed/sys/syscall/stm32/syscall_dispatch.ccore/embed/sys/syscall/stm32/syscall_stubs.cInspect captured patch +30 / −25
diff --git a/core/embed/io/haptic/drv262x/drv262x.c b/core/embed/io/haptic/drv262x/drv262x.c
index 223028b3..075a2cee 100644
--- a/core/embed/io/haptic/drv262x/drv262x.c
+++ b/core/embed/io/haptic/drv262x/drv262x.c
@@ -23,6 +23,7 @@
#include <io/haptic.h>
#include <io/i2c_bus.h>
+#include <rtl/logging.h>
#include <sys/systick.h>
#include "drv262x.h"
@@ -30,6 +31,8 @@
// Actuator configuration
#include DRV262X_ACTUATOR
+LOG_DECLARE(haptic_driver);
+
#ifdef KERNEL_MODE
// Maximum amplitude of the vibration effect
@@ -592,6 +595,7 @@ ts_t haptic_init(void) {
TSH_RETURN;
cleanup:
+ LOG_WARN("Haptic driver initialization failed, cleaning up");
haptic_deinit();
TSH_RETURN;
}
diff --git a/core/embed/projects/bootloader/main.c b/core/embed/projects/bootloader/main.c
index 10c3025d..35890881 100644
--- a/core/embed/projects/bootloader/main.c
+++ b/core/embed/projects/bootloader/main.c
@@ -161,7 +161,8 @@ static secbool boot_sequence(void) {
#endif
#ifdef USE_HAPTIC
- haptic_init();
+ ts_t status = haptic_init();
+ UNUSED(status);
#endif
#ifdef USE_RTC
diff --git a/core/embed/projects/kernel/main.c b/core/embed/projects/kernel/main.c
index 10532a4f..d219ba81 100644
--- a/core/embed/projects/kernel/main.c
+++ b/core/embed/projects/kernel/main.c
@@ -170,7 +170,8 @@ void drivers_init() {
#endif
#ifdef USE_HAPTIC
- haptic_init();
+ ts_t status = haptic_init();
+ UNUSED(status);
#endif
#ifdef USE_BLE
diff --git a/core/embed/projects/prodtest/main.c b/core/embed/projects/prodtest/main.c
index 595e58a7..32031be5 100644
--- a/core/embed/projects/prodtest/main.c
+++ b/core/embed/projects/prodtest/main.c
@@ -184,8 +184,7 @@ static void drivers_init(void) {
#ifdef USE_HAPTIC
ts_t status;
status = haptic_init();
- if (ts_error(status)) {
- }
+ UNUSED(status);
#endif
#ifdef USE_RGB_LED
rgb_led_init();
diff --git a/core/embed/sys/dbg/inc/sys/syslog_config.h b/core/embed/sys/dbg/inc/sys/syslog_config.h
index 1839bad1..7405ee0f 100644
--- a/core/embed/sys/dbg/inc/sys/syslog_config.h
+++ b/core/embed/sys/dbg/inc/sys/syslog_config.h
@@ -52,6 +52,10 @@
#define SYSLOG_display_driver_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
#endif
+#ifndef SYSLOG_haptic_driver_MAX_LOG_LEVEL
+#define SYSLOG_haptic_driver_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
+#endif
+
#ifndef SYSLOG_ble_driver_MAX_LOG_LEVEL
#define SYSLOG_ble_driver_MAX_LOG_LEVEL SYSLOG_DEFAULT_LOG_LEVEL
#endif
diff --git a/core/embed/sys/suspend/stm32u5/suspend_io.c b/core/embed/sys/suspend/stm32u5/suspend_io.c
index 77d7243c..9e0734c5 100644
--- a/core/embed/sys/suspend/stm32u5/suspend_io.c
+++ b/core/embed/sys/suspend/stm32u5/suspend_io.c
@@ -156,7 +156,8 @@ void resume_drivers(const power_save_wakeup_params_t *wakeup_params) {
touch_init();
#endif
#ifdef USE_HAPTIC
- haptic_init();
+ ts_t status = haptic_init();
+ ensure_ok(status, "haptic driver initialization failed");
#endif
#ifdef USE_RGB_LED
rgb_led_resume(&wakeup_params->rgb_led);
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index a8859d10..4ced8862 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -101,7 +101,6 @@ typedef enum {
SYSCALL_HAPTIC_SET_ENABLED,
SYSCALL_HAPTIC_GET_ENABLED,
- SYSCALL_HAPTIC_TEST,
SYSCALL_HAPTIC_PLAY,
SYSCALL_HAPTIC_PLAY_CUSTOM,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index acdc382f..58a995fc 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -398,28 +398,27 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
#ifdef USE_HAPTIC
case SYSCALL_HAPTIC_SET_ENABLED: {
bool enabled = (args[0] != 0);
- haptic_set_enabled(enabled);
+ ts_t status = haptic_set_enabled(enabled);
+ args[0] = ts_code(status);
} break;
case SYSCALL_HAPTIC_GET_ENABLED: {
args[0] = haptic_get_enabled();
} break;
- case SYSCALL_HAPTIC_TEST: {
- uint16_t duration_ms = (uint16_t)args[0];
- args[0] = haptic_test(duration_ms);
- } break;
-
case SYSCALL_HAPTIC_PLAY: {
haptic_effect_t effect = (haptic_effect_t)args[0];
- args[0] = haptic_play(effect);
+ ts_t status = haptic_play(effect);
+ args[0] = ts_code(status);
} break;
case SYSCALL_HAPTIC_PLAY_CUSTOM: {
int8_t amplitude_pct = (int8_t)args[0];
uint16_t duration_ms = (uint16_t)args[1];
- args[0] = haptic_play_custom(amplitude_pct, duration_ms);
+ ts_t status = haptic_play_custom(amplitude_pct, duration_ms);
+ args[0] = ts_code(status);
} break;
+
#endif
#ifdef USE_OPTIGA
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index cd21585f..6f66d572 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -393,25 +393,22 @@ rgb_led_effect_type_t rgb_led_effect_get_type(void) {
#include <io/haptic.h>
-void haptic_set_enabled(bool enabled) {
- syscall_invoke1((uint32_t)enabled, SYSCALL_HAPTIC_SET_ENABLED);
+ts_t haptic_set_enabled(bool enabled) {
+ return ts_make(
+ syscall_invoke1((uint32_t)enabled, SYSCALL_HAPTIC_SET_ENABLED));
}
bool haptic_get_enabled(void) {
return (bool)syscall_invoke0(SYSCALL_HAPTIC_GET_ENABLED);
}
-bool haptic_test(uint16_t duration_ms) {
- return (bool)syscall_invoke1(duration_ms, SYSCALL_HAPTIC_TEST);
+ts_t haptic_play(haptic_effect_t effect) {
+ return ts_make(syscall_invoke1((uint32_t)effect, SYSCALL_HAPTIC_PLAY));
}
-bool haptic_play(haptic_effect_t effect) {
- return (bool)syscall_invoke1((uint32_t)effect, SYSCALL_HAPTIC_PLAY);
-}
-
-bool haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms) {
- return (bool)syscall_invoke2((uint32_t)amplitude_pct, duration_ms,
- SYSCALL_HAPTIC_PLAY_CUSTOM);
+ts_t haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms) {
+ return ts_make(syscall_invoke2((uint32_t)amplitude_pct, duration_ms,
+ SYSCALL_HAPTIC_PLAY_CUSTOM));
}
#endif // USE_HAPTIC
Why this scored 26/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.