refactor(core): emulator socket handling
What changed, and why it matters
This commit is a code cleanup that moves emulator-only UDP socket handling for the Trezor hardware wallet emulator into a shared helper module. It affects only the Unix emulator build, not real Trezor devices. The change does not appear to fix or introduce a security vulnerability; it is a refactoring to reduce duplicated code between USB and BLE emulation layers.
No security action required. Treat as normal code-quality refactoring. If reviewing, verify that the new `sock.c` helper correctly preserves non-blocking behavior and that `ble_write` now returning a boolean (`r == len`) matches caller expectations, since the previous implementation returned the raw `ssize_t`.
Security signals we found
Refactoring of emulator-only networking code with no device-side impact
No changelog entry, consistent with internal cleanup
No mention of CVE, security bug, researcher credit, or advisory in commit or supplied references
Behavioral hardening: sendto short-write now returns -1; recvfrom zeroes buffer and returns 0 on error
Evidence from the diff
The patch refactors UDP socket creation, binding, polling, sendto/recvfrom, and teardown into a new emu_sock_t abstraction (core/embed/io/usb/inc/io/unix/sock.h and core/embed/io/usb/unix/sock.c). Both the Unix USB emulation (core/embed/io/usb/unix/usb.c) and Unix BLE emulation (core/embed/io/ble/unix/ble.c) are converted to use this abstraction. The build system (core/site_scons/models/unix_common.py) is updated to compile the new sock.c file. The behavior is largely preserved: sockets remain non-blocking UDP bound to loopback or TREZOR_UDP_IP with ports derived from TREZOR_UDP_PORT. One minor behavioral change is that sock_sendto now returns -1 on a short send instead of the actual byte count, and callers now compare against len; sock_recvfrom zeroes the receive buffer and returns 0 on error or no data. These are robustness/correctness improvements rather than security fixes.
Changed components
core/embed/io/usb/unix/usb.ccore/embed/io/ble/unix/ble.ccore/embed/io/usb/unix/sock.c (new)core/embed/io/usb/inc/io/unix/sock.h (new)core/site_scons/models/unix_common.pyInspect captured patch +158 / −145
diff --git a/core/embed/io/ble/unix/ble.c b/core/embed/io/ble/unix/ble.c
index b58770a8f..6564345d6 100644
--- a/core/embed/io/ble/unix/ble.c
+++ b/core/embed/io/ble/unix/ble.c
@@ -1,4 +1,5 @@
#include <io/ble.h>
+#include <io/unix/sock.h>
#include <sys/sysevent_source.h>
#include <trezor_rtl.h>
@@ -22,16 +23,8 @@ typedef struct {
bt_le_addr_t connected_addr;
bt_le_addr_t bonds[BLE_MAX_BONDS];
size_t bonds_len;
-
- uint16_t data_port;
- int data_sock;
- struct sockaddr_in data_si_me, data_si_other;
- socklen_t data_slen;
-
- uint16_t event_port;
- int event_sock;
- struct sockaddr_in event_si_me, event_si_other;
- socklen_t event_slen;
+ emu_sock_t data_sock;
+ emu_sock_t event_sock;
} ble_driver_t;
typedef struct {
@@ -93,6 +86,8 @@ static bool is_enabled(const ble_driver_t *drv) {
bool ble_init(void) {
ble_driver_t *drv = &g_ble_driver;
+ sock_init(&drv->data_sock);
+ sock_init(&drv->event_sock);
if (!syshandle_register(SYSHANDLE_BLE, &ble_handle_vmt, drv)) {
goto cleanup;
}
@@ -116,35 +111,13 @@ void ble_deinit(void) {
void ble_start(void) {
ble_driver_t *drv = &g_ble_driver;
memset(drv, 0, sizeof(*drv));
- drv->data_sock = -1;
- drv->event_sock = -1;
const char *ip = getenv("TREZOR_UDP_IP");
const char *port_base_str = getenv("TREZOR_UDP_PORT");
uint16_t port_base = port_base_str ? atoi(port_base_str) : 21324;
- drv->data_port = port_base + DATA_PORT_OFFSET;
- drv->event_port = port_base + EVENT_PORT_OFFSET;
- drv->data_sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
- drv->event_sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
-
- ensure(sectrue * (drv->data_sock >= 0), NULL);
- ensure(sectrue * (drv->event_sock >= 0), NULL);
-
- drv->data_si_me.sin_family = drv->event_si_me.sin_family = AF_INET;
- drv->data_si_me.sin_addr.s_addr = ip ? inet_addr(ip) : htonl(INADDR_LOOPBACK);
- drv->event_si_me.sin_addr.s_addr =
- ip ? inet_addr(ip) : htonl(INADDR_LOOPBACK);
- drv->data_si_me.sin_port = htons(drv->data_port);
- drv->event_si_me.sin_port = htons(drv->event_port);
-
- int ret = -1;
- ret = bind(drv->data_sock, (struct sockaddr *)&(drv->data_si_me),
- sizeof(struct sockaddr_in));
- ensure(sectrue * (ret == 0), NULL);
- ret = bind(drv->event_sock, (struct sockaddr *)&(drv->event_si_me),
- sizeof(struct sockaddr_in));
- ensure(sectrue * (ret == 0), NULL);
+ sock_start(&drv->data_sock, ip, port_base + DATA_PORT_OFFSET);
+ sock_start(&drv->event_sock, ip, port_base + EVENT_PORT_OFFSET);
drv->initialized = true;
}
@@ -155,14 +128,8 @@ void ble_stop(void) {
return;
}
- if (drv->data_sock >= 0) {
- close(drv->data_sock);
- drv->data_sock = -1;
- }
- if (drv->event_sock >= 0) {
- close(drv->event_sock);
- drv->event_sock = -1;
- }
+ sock_stop(&drv->data_sock);
+ sock_stop(&drv->event_sock);
drv->initialized = false;
}
@@ -179,14 +146,9 @@ static bool send_to_emu(char cmdtype) {
}
memcpy(&command.adv_name, drv->adv_name, BLE_ADV_NAME_LEN);
- ssize_t r = -2;
- if (drv->event_slen > 0) {
- r = sendto(drv->event_sock, &command, sizeof(command), MSG_DONTWAIT,
- (const struct sockaddr *)&(drv->event_si_other),
- drv->event_slen);
- }
+ ssize_t r = sock_sendto(&drv->event_sock, &command, sizeof(command));
if (r != sizeof(command)) {
- printf("unix/ble: failed to write command %c: %d\n", cmdtype, (int)r);
+ printf("unix/ble: failed to write command %c: %zd\n", cmdtype, r);
}
return true;
@@ -284,11 +246,9 @@ bool ble_get_event(ble_event_t *event) {
if (!is_enabled(drv)) {
return false;
}
- struct sockaddr_in si;
- socklen_t sl = sizeof(si);
+
uint8_t buf[sizeof(ble_event_t)] = {0};
- ssize_t r = recvfrom(drv->event_sock, buf, sizeof(buf), MSG_DONTWAIT,
- (struct sockaddr *)&si, &sl);
+ ssize_t r = sock_recvfrom(&drv->event_sock, buf, sizeof(buf));
if (r <= 0) {
return false;
} else if (r > sizeof(ble_event_t)) {
@@ -296,9 +256,6 @@ bool ble_get_event(ble_event_t *event) {
return false;
}
- drv->event_si_other = si;
- drv->event_slen = sl;
-
const ble_event_t *e = (ble_event_t *)buf;
switch (e->type) {
@@ -398,11 +355,7 @@ bool ble_can_write(void) {
return false;
}
- struct pollfd fds[] = {
- {drv->data_sock, POLLOUT, 0},
- };
- int r = poll(fds, 1, 0);
- return (r > 0);
+ return sock_can_send(&drv->data_sock);
}
bool ble_write(const uint8_t *data, uint16_t len) {
@@ -416,12 +369,8 @@ bool ble_write(const uint8_t *data, uint16_t len) {
return false;
}
- ssize_t r = len;
- if (drv->data_slen > 0) {
- r = sendto(drv->data_sock, data, len, MSG_DONTWAIT,
- (const struct sockaddr *)&(drv->data_si_other), drv->data_slen);
- }
- return r;
+ ssize_t r = sock_sendto(&drv->data_sock, data, len);
+ return r == len;
}
bool ble_can_read(void) {
@@ -430,11 +379,7 @@ bool ble_can_read(void) {
return false;
}
- struct pollfd fds[] = {
- {drv->data_sock, POLLIN, 0},
- };
- int r = poll(fds, 1, 0);
- return (r > 0);
+ return sock_can_recv(&drv->data_sock);
}
uint32_t ble_read(uint8_t *data, uint16_t max_len) {
@@ -448,18 +393,12 @@ uint32_t ble_read(uint8_t *data, uint16_t max_len) {
return false;
}
- struct sockaddr_in si;
- socklen_t sl = sizeof(si);
- uint8_t buf[max_len];
- memset(buf, 0, max_len);
- ssize_t r = recvfrom(drv->data_sock, buf, sizeof(buf), MSG_DONTWAIT,
- (struct sockaddr *)&si, &sl);
+ uint8_t buf[max_len] = {};
+ ssize_t r = sock_recvfrom(&drv->data_sock, buf, sizeof(buf));
if (r <= 0) {
return 0;
}
- drv->data_si_other = si;
- drv->data_slen = sl;
memcpy(data, buf, r);
return r;
}
@@ -539,11 +478,7 @@ static void on_ble_poll(void *context, bool read_awaited, bool write_awaited) {
// check if you can read from event socket
if (is_enabled(drv)) {
- struct pollfd fds[] = {
- {drv->event_sock, POLLIN, 0},
- };
- int r = poll(fds, 1, 0);
- ready = (r > 0);
+ ready = sock_can_recv(&drv->event_sock);
}
syshandle_signal_read_ready(SYSHANDLE_BLE, &ready);
diff --git a/core/embed/io/usb/inc/io/unix/sock.h b/core/embed/io/usb/inc/io/unix/sock.h
new file mode 100644
index 000000000..ac8174feb
--- /dev/null
+++ b/core/embed/io/usb/inc/io/unix/sock.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
+
+/// Emulator datagram socket, for USB and BLE. Currently uses UDP but can be
+/// possibly switched to unix datagram sockets.
+typedef struct {
+ /// Port number.
+ uint16_t port;
+ /// Socket file descriptor.
+ int sock;
+ /// Emulator host+port.
+ struct sockaddr_in si_me;
+ /// Address of the other side of the connection. Set based on the last packet
+ /// received.
+ struct sockaddr_in si_other;
+ /// Length of si_other. Before first packet is received this is 0 meaning we
+ /// don't know the addres of the other side.
+ socklen_t slen;
+} emu_sock_t;
+
+void sock_init(emu_sock_t *sock);
+
+void sock_start(emu_sock_t *sock, const char *ip, uint16_t port);
+
+void sock_stop(emu_sock_t *sock);
+
+bool sock_can_send(emu_sock_t *sock);
+
+bool sock_can_recv(emu_sock_t *sock);
+
+ssize_t sock_sendto(emu_sock_t *sock, const void *data, size_t len);
+
+ssize_t sock_recvfrom(emu_sock_t *sock, uint8_t *data, size_t max_len);
diff --git a/core/embed/io/usb/unix/sock.c b/core/embed/io/usb/unix/sock.c
new file mode 100644
index 000000000..ecf9a36e7
--- /dev/null
+++ b/core/embed/io/usb/unix/sock.c
@@ -0,0 +1,80 @@
+#include "io/unix/sock.h"
+#include "trezor_rtl.h"
+
+#include <string.h>
+
+void sock_init(emu_sock_t *sock) {
+ memset(sock, 0, sizeof(*sock));
+ sock->sock = -1;
+}
+
+void sock_start(emu_sock_t *sock, const char *ip, uint16_t port) {
+ sock->port = port;
+ sock->sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
+
+ ensure(sectrue * (sock->sock >= 0), NULL);
+
+ int ret = fcntl(sock->sock, F_SETFL, O_NONBLOCK);
+ ensure(sectrue * (ret != -1), NULL);
+
+ sock->si_me.sin_family = AF_INET;
+ sock->si_me.sin_addr.s_addr = ip ? inet_addr(ip) : htonl(INADDR_LOOPBACK);
+ sock->si_me.sin_port = htons(sock->port);
+
+ ret = bind(sock->sock, (struct sockaddr *)&(sock->si_me),
+ sizeof(struct sockaddr_in));
+ ensure(sectrue * (ret == 0), NULL);
+}
+
+void sock_stop(emu_sock_t *sock) {
+ if (sock->sock >= 0) {
+ close(sock->sock);
+ sock->sock = -1;
+ }
+}
+
+bool sock_can_send(emu_sock_t *sock) {
+ if (sock->slen == 0) {
+ return true;
+ }
+ struct pollfd fds[] = {
+ {sock->sock, POLLOUT, 0},
+ };
+ int r = poll(fds, 1, 0);
+ return (r > 0);
+}
+
+bool sock_can_recv(emu_sock_t *sock) {
+ struct pollfd fds[] = {
+ {sock->sock, POLLIN, 0},
+ };
+ int r = poll(fds, 1, 0);
+ return (r > 0);
+}
+
+ssize_t sock_sendto(emu_sock_t *sock, const void *data, size_t len) {
+ if (sock->slen > 0) {
+ ssize_t r = sendto(sock->sock, data, len, MSG_DONTWAIT,
+ (const struct sockaddr *)&(sock->si_other), sock->slen);
+ if (r != len) {
+ return -1;
+ }
+ return r;
+ }
+ return len;
+}
+
+ssize_t sock_recvfrom(emu_sock_t *sock, uint8_t *data, size_t max_len) {
+ struct sockaddr_in si;
+ socklen_t sl = sizeof(si);
+ memset(data, 0, max_len);
+ ssize_t r = recvfrom(sock->sock, data, max_len, MSG_DONTWAIT,
+ (struct sockaddr *)&si, &sl);
+ if (r <= 0) {
+ return 0;
+ }
+
+ sock->si_other = si;
+ sock->slen = sl;
+ return r;
+}
diff --git a/core/embed/io/usb/unix/usb.c b/core/embed/io/usb/unix/usb.c
index b45a7553a..566376171 100644
--- a/core/embed/io/usb/unix/usb.c
+++ b/core/embed/io/usb/unix/usb.c
@@ -28,6 +28,7 @@
#include <time.h>
#include <unistd.h>
+#include <io/unix/sock.h>
#include <io/usb.h>
#include <io/usb_hid.h>
#include <io/usb_vcp.h>
@@ -50,9 +51,7 @@ typedef struct {
syshandle_t handle;
usb_iface_type_t type;
uint16_t port;
- int sock;
- struct sockaddr_in si_me, si_other;
- socklen_t slen;
+ emu_sock_t sock;
uint8_t msg[64];
int msg_len;
} usb_iface_t;
@@ -69,11 +68,8 @@ secbool usb_init(const usb_dev_info_t *dev_info) {
iface->handle = 0;
iface->type = USB_IFACE_TYPE_DISABLED;
iface->port = 0;
- iface->sock = -1;
- memzero(&iface->si_me, sizeof(struct sockaddr_in));
- memzero(&iface->si_other, sizeof(struct sockaddr_in));
+ sock_init(&iface->sock);
memzero(&iface->msg, sizeof(usb_ifaces[i].msg));
- iface->slen = 0;
iface->msg_len = 0;
}
return sectrue;
@@ -94,22 +90,7 @@ secbool usb_start(const usb_start_params_t *params) {
continue;
}
- iface->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- ensure(sectrue * (iface->sock >= 0), NULL);
-
- fcntl(iface->sock, F_SETFL, O_NONBLOCK);
-
- iface->si_me.sin_family = AF_INET;
- if (ip) {
- iface->si_me.sin_addr.s_addr = inet_addr(ip);
- } else {
- iface->si_me.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- }
- iface->si_me.sin_port = htons(iface->port);
-
- ensure(sectrue * (0 == bind(iface->sock, (struct sockaddr *)&iface->si_me,
- sizeof(struct sockaddr_in))),
- NULL);
+ sock_start(&iface->sock, ip, iface->port);
ensure(sectrue *
syshandle_register(iface->handle, &usb_iface_handle_vmt, iface),
@@ -122,11 +103,8 @@ secbool usb_start(const usb_start_params_t *params) {
void usb_stop(void) {
for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) {
usb_iface_t *iface = &usb_ifaces[i];
- if (iface->sock >= 0) {
- close(iface->sock);
- iface->sock = -1;
- syshandle_unregister(iface->handle);
- }
+ sock_stop(&iface->sock);
+ syshandle_unregister(iface->handle);
}
}
@@ -174,48 +152,31 @@ static secbool usb_emulated_poll_read(usb_iface_t *iface) {
return sectrue;
}
- struct pollfd fds[] = {
- {iface->sock, POLLIN, 0},
- };
- int res = poll(fds, 1, 0);
-
- if (res <= 0) {
+ if (!sock_can_recv(&iface->sock)) {
return secfalse;
}
- struct sockaddr_in si;
- socklen_t sl = sizeof(si);
- ssize_t r = recvfrom(iface->sock, iface->msg, sizeof(iface->msg),
- MSG_DONTWAIT, (struct sockaddr *)&si, &sl);
- if (r <= 0) {
+ size_t len = sock_recvfrom(&iface->sock, iface->msg, sizeof(iface->msg));
+ if (!len) {
return secfalse;
}
- iface->si_other = si;
- iface->slen = sl;
static const char *ping_req = "PINGPING";
static const char *ping_resp = "PONGPONG";
- if (r == strlen(ping_req) &&
+ if (len == strlen(ping_req) &&
0 == memcmp(ping_req, iface->msg, strlen(ping_req))) {
- if (iface->slen > 0) {
- sendto(iface->sock, ping_resp, strlen(ping_resp), MSG_DONTWAIT,
- (const struct sockaddr *)&iface->si_other, iface->slen);
- }
+ sock_sendto(&iface->sock, (const uint8_t *)ping_resp, strlen(ping_resp));
memzero(iface->msg, sizeof(iface->msg));
return secfalse;
}
- iface->msg_len = r;
+ iface->msg_len = len;
return sectrue;
}
static secbool usb_emulated_poll_write(usb_iface_t *iface) {
- struct pollfd fds[] = {
- {iface->sock, POLLOUT, 0},
- };
- int r = poll(fds, 1, 0);
- return sectrue * (r > 0);
+ return sectrue * sock_can_send(&iface->sock);
}
static int usb_emulated_read(usb_iface_t *iface, uint8_t *buf, uint32_t len) {
@@ -238,14 +199,9 @@ static int usb_emulated_read(usb_iface_t *iface, uint8_t *buf, uint32_t len) {
return 0;
}
-static int usb_emulated_write(usb_iface_t *iface, const uint8_t *buf,
- uint32_t len) {
- ssize_t r = len;
- if (iface->slen > 0) {
- r = sendto(iface->sock, buf, len, MSG_DONTWAIT,
- (const struct sockaddr *)&iface->si_other, iface->slen);
- }
- return r;
+static ssize_t usb_emulated_write(usb_iface_t *iface, const uint8_t *buf,
+ uint32_t len) {
+ return sock_sendto(&iface->sock, buf, len);
}
secbool usb_configured(void) {
diff --git a/core/site_scons/models/unix_common.py b/core/site_scons/models/unix_common.py
index 5b5b9c768..c5006fcab 100644
--- a/core/site_scons/models/unix_common.py
+++ b/core/site_scons/models/unix_common.py
@@ -33,6 +33,7 @@ def unix_common_files(env, features_wanted, defines, sources, paths):
sources += [
"embed/io/display/unix/display_driver.c",
+ "embed/io/usb/unix/sock.c",
"embed/sec/random_delays/unix/random_delays.c",
"embed/sec/secret/unix/secret.c",
"embed/sec/secret/unix/secret_keys.c",
Why this scored 21/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.