feat(core/ble): expose get bond list to coreapp
What changed, and why it matters
This commit adds a new system call that lets the Trezor core application read the list of Bluetooth devices the hardware has bonded with. It is a feature addition, not a fix. The code includes a memory-access check before copying the bond list into the caller's buffer, which is the expected defensive pattern for this firmware's syscall layer. There is no direct evidence in the commit that this is a security patch or that it addresses a known vulnerability.
Treat as a routine feature commit. Review the underlying ble_get_bond_list() implementation for correct bounds handling and ensure the returned bond count cannot exceed the caller's buffer. No immediate security response is indicated by this diff alone.
Security signals we found
New syscall added to the trusted kernel boundary
User-supplied pointer and size are validated with probe_write_access before privileged code writes to them
No changelog entry provided, limiting public context
No vendor statement or CVE reference present in the commit
Evidence from the diff
The change exposes ble_get_bond_list() from the privileged/secure world to the unprivileged coreapp via a new syscall SYSCALL_BLE_GET_BOND_LIST. The dispatcher passes a user-supplied pointer (args[0]) and count (args[1]) to ble_get_bond_list__verified(), which uses probe_write_access(bonds, sizeof(bt_le_addr_t) * count) to validate the output buffer before invoking the underlying BLE function. This follows the same verifier pattern used by neighboring BLE syscalls (ble_unpair__verified, ble_set_name__verified). No input sanitization beyond the write-probe is visible, but the operation is read-only into a user buffer and does not appear to trust user-supplied data as kernel input.
Changed components
core/embed/sys/syscall/stm32/syscall_dispatch.ccore/embed/sys/syscall/stm32/syscall_stubs.ccore/embed/sys/syscall/stm32/syscall_verifiers.ccore/embed/sys/syscall/stm32/syscall_verifiers.hcore/embed/sys/syscall/inc/sys/syscall_numbers.hInspect captured patch +27 / −0
diff --git a/core/embed/sys/syscall/inc/sys/syscall_numbers.h b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
index 41c1a07e0..b3b8c5eb0 100644
--- a/core/embed/sys/syscall/inc/sys/syscall_numbers.h
+++ b/core/embed/sys/syscall/inc/sys/syscall_numbers.h
@@ -143,6 +143,7 @@ typedef enum {
SYSCALL_BLE_READ,
SYSCALL_BLE_SET_NAME,
SYSCALL_BLE_UNPAIR,
+ SYSCALL_BLE_GET_BOND_LIST,
SYSCALL_NRF_UPDATE_REQUIRED,
SYSCALL_NRF_UPDATE,
diff --git a/core/embed/sys/syscall/stm32/syscall_dispatch.c b/core/embed/sys/syscall/stm32/syscall_dispatch.c
index 186005ee5..3336d5b25 100644
--- a/core/embed/sys/syscall/stm32/syscall_dispatch.c
+++ b/core/embed/sys/syscall/stm32/syscall_dispatch.c
@@ -738,6 +738,12 @@ __attribute((no_stack_protector)) void syscall_handler(uint32_t *args,
const bt_le_addr_t *addr = (const bt_le_addr_t *)args[0];
args[0] = ble_unpair__verified(addr);
} break;
+
+ case SYSCALL_BLE_GET_BOND_LIST: {
+ bt_le_addr_t *list = (bt_le_addr_t *)args[0];
+ size_t list_size = args[1];
+ args[0] = ble_get_bond_list__verified(list, list_size);
+ } break;
#endif
#ifdef USE_NRF
diff --git a/core/embed/sys/syscall/stm32/syscall_stubs.c b/core/embed/sys/syscall/stm32/syscall_stubs.c
index 5ec82dffc..846926730 100644
--- a/core/embed/sys/syscall/stm32/syscall_stubs.c
+++ b/core/embed/sys/syscall/stm32/syscall_stubs.c
@@ -700,6 +700,11 @@ bool ble_unpair(const bt_le_addr_t *addr) {
return (bool)syscall_invoke1((uint32_t)addr, SYSCALL_BLE_UNPAIR);
}
+uint8_t ble_get_bond_list(bt_le_addr_t *bonds, size_t count) {
+ return (uint8_t)syscall_invoke2((uint32_t)bonds, count,
+ SYSCALL_BLE_GET_BOND_LIST);
+}
+
#endif
#ifdef USE_NRF
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.c b/core/embed/sys/syscall/stm32/syscall_verifiers.c
index 660176fdb..233a16ffd 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.c
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.c
@@ -868,6 +868,19 @@ access_violation:
return false;
}
+uint8_t ble_get_bond_list__verified(bt_le_addr_t *bonds, size_t count) {
+ if (!probe_write_access(bonds, sizeof(bt_le_addr_t) * count)) {
+ goto access_violation;
+ }
+
+ return ble_get_bond_list(bonds, count);
+
+access_violation:
+ apptask_access_violation();
+
+ return 0;
+}
+
#endif
// ---------------------------------------------------------------------
diff --git a/core/embed/sys/syscall/stm32/syscall_verifiers.h b/core/embed/sys/syscall/stm32/syscall_verifiers.h
index 9f5445550..a0ba00b10 100644
--- a/core/embed/sys/syscall/stm32/syscall_verifiers.h
+++ b/core/embed/sys/syscall/stm32/syscall_verifiers.h
@@ -211,6 +211,8 @@ void ble_set_name__verified(const uint8_t *name, size_t len);
bool ble_unpair__verified(const bt_le_addr_t *addr);
+uint8_t ble_get_bond_list__verified(bt_le_addr_t *bonds, size_t count);
+
#endif
// ---------------------------------------------------------------------
Why this scored 20/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.