feat(core): implement micropython ipc wrapper
What changed, and why it matters
This commit adds a new internal feature to the Trezor firmware: a MicroPython wrapper that lets Python code send and receive inter-process communication (IPC) messages to another system task. It is a feature implementation, not a bug fix, and nothing in the commit or supplied references indicates it addresses a security vulnerability.
No security action required. Review the new IPC surface during normal secure-development lifecycle checks, especially validation of remote task IDs and message data handling, since IPC can become a future attack surface.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit introduces modtrezorio-ipc.h, exposing ipc_send() and an IpcMessage class to MicroPython, plus IPC2_EVENT polling support in modtrezorio-poll.h. It also reorganizes some includes and mock type stubs. The change is gated behind USE_IPC and appears to be foundational plumbing for a multi-task architecture. There is no patch of an existing vulnerability, no bounds-checking fix, no privilege change, and no vendor security disclosure.
Changed components
core/embed/upymod/modtrezorio/modtrezorio-ipc.hcore/embed/upymod/modtrezorio/modtrezorio-poll.hcore/embed/upymod/modtrezorio/modtrezorio.ccore/mocks/generated/trezorio/__init__.pyiInspect captured patch +188 / −6
diff --git a/core/embed/upymod/modtrezorio/modtrezorio-fatfs.h b/core/embed/upymod/modtrezorio/modtrezorio-fatfs.h
index 4d9c16823..3efa1b33f 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio-fatfs.h
+++ b/core/embed/upymod/modtrezorio/modtrezorio-fatfs.h
@@ -17,7 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "embed/upymod/trezorobj.h"
#include "py/mperrno.h"
#include "py/objstr.h"
diff --git a/core/embed/upymod/modtrezorio/modtrezorio-ipc.h b/core/embed/upymod/modtrezorio/modtrezorio-ipc.h
new file mode 100644
index 000000000..caacb69e9
--- /dev/null
+++ b/core/embed/upymod/modtrezorio/modtrezorio-ipc.h
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <trezor_rtl.h>
+
+#include <sys/ipc.h>
+
+/// package: trezorio.__init__
+
+/// def ipc_send(remote: int, fn: int, data: AnyBytes) -> None:
+/// """
+/// Sends an IPC message to the specified remote task.
+/// """
+STATIC mp_obj_t mod_trezorio_ipc_send(mp_obj_t remote_obj, mp_obj_t fn_obj,
+ mp_obj_t data_obj) {
+ mp_buffer_info_t bufinfo = {0};
+ mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ);
+
+ systask_id_t remote = (systask_id_t)mp_obj_get_int(remote_obj);
+ uint32_t fn = (uint32_t)mp_obj_get_int(fn_obj);
+
+ if (!ipc_send(remote, fn, bufinfo.buf, bufinfo.len)) {
+ mp_raise_msg(&mp_type_RuntimeError,
+ MP_ERROR_TEXT("Failed to send IPC message."));
+ }
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorio_ipc_send_obj,
+ mod_trezorio_ipc_send);
+
+/// class IpcMessage:
+/// """
+/// IPC message structure.
+/// """
+typedef struct _mp_obj_IpcMessage_t {
+ mp_obj_base_t base;
+ ipc_message_t message;
+} mp_obj_IpcMessage_t;
+
+/// def fn(self) -> int:
+/// """
+/// Returns the function number.
+/// """
+STATIC mp_obj_t mod_trezorio_IpcMessage_fn(mp_obj_t self) {
+ mp_obj_IpcMessage_t *o = MP_OBJ_TO_PTR(self);
+ return MP_OBJ_NEW_SMALL_INT(o->message.fn); // !@# uint
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_IpcMessage_fn_obj,
+ mod_trezorio_IpcMessage_fn);
+
+/// def remote(self) -> int:
+/// """
+/// Returns the remote task ID.
+/// """
+STATIC mp_obj_t mod_trezorio_IpcMessage_remote(mp_obj_t self) {
+ mp_obj_IpcMessage_t *o = MP_OBJ_TO_PTR(self);
+ return MP_OBJ_NEW_SMALL_INT(o->message.remote);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_IpcMessage_remote_obj,
+ mod_trezorio_IpcMessage_remote);
+
+/// def free(self) -> None:
+/// """
+/// Frees the IPC message resources.
+/// """
+STATIC mp_obj_t mod_trezorio_IpcMessage_free(mp_obj_t self) {
+ mp_obj_IpcMessage_t *o = MP_OBJ_TO_PTR(self);
+
+ ipc_message_free(&o->message);
+ memset(&o->message, 0, sizeof(ipc_message_t));
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_IpcMessage_free_obj,
+ mod_trezorio_IpcMessage_free);
+
+/// def data(self) -> bytes:
+/// """
+/// Returns the IPC message data as bytes.
+/// """
+STATIC mp_obj_t mod_trezorio_IpcMessage_data(mp_obj_t self) {
+ mp_obj_IpcMessage_t *o = MP_OBJ_TO_PTR(self);
+ return mp_obj_new_bytes(o->message.data, o->message.size);
+}
+
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_IpcMessage_data_obj,
+ mod_trezorio_IpcMessage_data);
+
+STATIC const mp_rom_map_elem_t mod_trezorio_IpcMessage_locals_dict_table[] = {
+ {MP_ROM_QSTR(MP_QSTR_remote),
+ MP_ROM_PTR(&mod_trezorio_IpcMessage_remote_obj)},
+ {MP_ROM_QSTR(MP_QSTR_fn), MP_ROM_PTR(&mod_trezorio_IpcMessage_fn_obj)},
+ {MP_ROM_QSTR(MP_QSTR_data), MP_ROM_PTR(&mod_trezorio_IpcMessage_data_obj)},
+ {MP_ROM_QSTR(MP_QSTR_free), MP_ROM_PTR(&mod_trezorio_IpcMessage_free_obj)},
+};
+STATIC MP_DEFINE_CONST_DICT(mod_trezorio_IpcMessage_locals_dict,
+ mod_trezorio_IpcMessage_locals_dict_table);
+
+STATIC const mp_obj_type_t mod_trezorio_IpcMessage_type = {
+ {&mp_type_type},
+ .name = MP_QSTR_IpcMessage,
+ .locals_dict = (void *)&mod_trezorio_IpcMessage_locals_dict,
+};
diff --git a/core/embed/upymod/modtrezorio/modtrezorio-poll.h b/core/embed/upymod/modtrezorio/modtrezorio-poll.h
index 3d192baed..f5f0e41b5 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio-poll.h
+++ b/core/embed/upymod/modtrezorio/modtrezorio-poll.h
@@ -36,7 +36,9 @@
#include <io/power_manager.h>
#endif
-#include "embed/upymod/trezorobj.h"
+#ifdef USE_IPC
+#include <sys/ipc.h>
+#endif
#define POLL_READ (0x0000)
#define POLL_WRITE (0x0100)
@@ -129,6 +131,20 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref,
return mp_const_false;
}
+#ifdef USE_IPC
+ if (signalled.read_ready & (1 << SYSHANDLE_IPC2)) {
+ ipc_message_t message = {.remote = 2};
+ if (ipc_try_receive(&message)) {
+ mp_obj_IpcMessage_t *o =
+ mp_obj_malloc(mp_obj_IpcMessage_t, &mod_trezorio_IpcMessage_type);
+ o->message = message;
+ ret->items[0] = MP_OBJ_NEW_SMALL_INT(SYSHANDLE_IPC2);
+ ret->items[1] = MP_OBJ_FROM_PTR(o);
+ return mp_const_true;
+ }
+ }
+#endif
+
#ifdef USE_TOUCH
if (signalled.read_ready & (1 << SYSHANDLE_TOUCH)) {
const uint32_t evt = touch_get_event();
diff --git a/core/embed/upymod/modtrezorio/modtrezorio-sdcard.h b/core/embed/upymod/modtrezorio/modtrezorio-sdcard.h
index cf0890006..3f10ff125 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio-sdcard.h
+++ b/core/embed/upymod/modtrezorio/modtrezorio-sdcard.h
@@ -17,7 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "embed/upymod/trezorobj.h"
#include "py/mperrno.h"
#include <io/sdcard.h>
diff --git a/core/embed/upymod/modtrezorio/modtrezorio-usb-if.h b/core/embed/upymod/modtrezorio/modtrezorio-usb-if.h
index da3ef0a1c..0c8ce11e6 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio-usb-if.h
+++ b/core/embed/upymod/modtrezorio/modtrezorio-usb-if.h
@@ -17,6 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <sys/sysevent.h>
+
/// package: trezorio.__init__
/// class USBIF:
diff --git a/core/embed/upymod/modtrezorio/modtrezorio.c b/core/embed/upymod/modtrezorio/modtrezorio.c
index 08533ed54..3260c2cf3 100644
--- a/core/embed/upymod/modtrezorio/modtrezorio.c
+++ b/core/embed/upymod/modtrezorio/modtrezorio.c
@@ -45,8 +45,9 @@ uint32_t last_touch_sample_time = 0;
mp_raise_ValueError((mp_rom_error_text_t)msg); \
}
+#include "embed/upymod/trezorobj.h"
+
// clang-format off
-#include "modtrezorio-poll.h"
#include "modtrezorio-usb.h"
#include "modtrezorio-usb-if.h"
// clang-format on
@@ -63,9 +64,13 @@ uint32_t last_touch_sample_time = 0;
#ifdef USE_POWER_MANAGER
#include "modtrezorio-pm.h"
#endif
+#ifdef USE_IPC
+#include "modtrezorio-ipc.h"
+#endif
+#include "modtrezorio-poll.h"
/// package: trezorio.__init__
-/// from . import fatfs, haptic, sdcard, ble, pm, rgb_led
+/// from . import fatfs, haptic, sdcard, ble, pm, rgb_led, ipc
/// POLL_READ: int # wait until interface is readable and return read data
/// POLL_WRITE: int # wait until interface is writable
@@ -75,6 +80,8 @@ uint32_t last_touch_sample_time = 0;
///
/// PM_EVENT: int # interface id for power manager events
///
+/// IPC2_EVENT: int # interface id for IPC2 events
+///
/// TOUCH: int # interface id of the touch events
/// TOUCH_START: int # event id of touch start event
/// TOUCH_MOVE: int # event id of touch move event
@@ -129,6 +136,10 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
{MP_ROM_QSTR(MP_QSTR_pm), MP_ROM_PTR(&mod_trezorio_pm_module)},
{MP_ROM_QSTR(MP_QSTR_PM_EVENT), MP_ROM_INT(SYSHANDLE_POWER_MANAGER)},
#endif
+#ifdef USE_IPC
+ {MP_ROM_QSTR(MP_QSTR_IPC2_EVENT), MP_ROM_INT(SYSHANDLE_IPC2)},
+ {MP_ROM_QSTR(MP_QSTR_ipc_send), MP_ROM_PTR(&mod_trezorio_ipc_send_obj)},
+#endif
{MP_ROM_QSTR(MP_QSTR_USB), MP_ROM_PTR(&mod_trezorio_USB_type)},
{MP_ROM_QSTR(MP_QSTR_USBIF), MP_ROM_PTR(&mod_trezorio_USBIF_type)},
diff --git a/core/mocks/generated/trezorio/__init__.pyi b/core/mocks/generated/trezorio/__init__.pyi
index 06ca78047..1e87541ea 100644
--- a/core/mocks/generated/trezorio/__init__.pyi
+++ b/core/mocks/generated/trezorio/__init__.pyi
@@ -2,6 +2,40 @@ from typing import *
from buffer_types import *
+# upymod/modtrezorio/modtrezorio-ipc.h
+def ipc_send(remote: int, fn: int, data: AnyBytes) -> None:
+ """
+ Sends an IPC message to the specified remote task.
+ """
+
+
+# upymod/modtrezorio/modtrezorio-ipc.h
+class IpcMessage:
+ """
+ IPC message structure.
+ """
+
+ def fn(self) -> int:
+ """
+ Returns the function number.
+ """
+
+ def remote(self) -> int:
+ """
+ Returns the remote task ID.
+ """
+
+ def free(self) -> None:
+ """
+ Frees the IPC message resources.
+ """
+
+ def data(self) -> bytes:
+ """
+ Returns the IPC message data as bytes.
+ """
+
+
# upymod/modtrezorio/modtrezorio-poll.h
def poll(ifaces: Iterable[int], list_ref: list, timeout_ms: int) -> bool:
"""
@@ -79,7 +113,7 @@ class USB:
"""
Cleans up the USB stack.
"""
-from . import fatfs, haptic, sdcard, ble, pm, rgb_led
+from . import fatfs, haptic, sdcard, ble, pm, rgb_led, ipc
POLL_READ: int # wait until interface is readable and return read data
POLL_WRITE: int # wait until interface is writable
@@ -88,6 +122,8 @@ BLE_EVENT: int # interface id for BLE events
PM_EVENT: int # interface id for power manager events
+IPC2_EVENT: int # interface id for IPC2 events
+
TOUCH: int # interface id of the touch events
TOUCH_START: int # event id of touch start event
TOUCH_MOVE: int # event id of touch move event
Why this scored 11/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.