usb: avoid stdio overhead and buffering when reading ota data
What changed, and why it matters
This commit changes how a Blockstream Jade hardware wallet reads firmware update files from USB storage. It switches from using standard C file reading (fopen/fread/fclose) to lower-level Unix file reading (open/read/close). The stated goal is to reduce memory overhead and buffering during over-the-air (OTA) firmware updates. There is no direct evidence in the commit that this fixes a security vulnerability; it appears to be a performance or robustness improvement.
Treat as a routine maintenance/optimization commit unless additional context emerges showing the stdio buffering caused exploitable behavior. If reviewing for security, verify that open()/read()/close() error paths correctly propagate failures to the OTA state machine and that partial reads are handled safely. No urgent action required.
Security signals we found
Change is in firmware update (OTA) code path, which is security-sensitive
Removes stdio buffering layer that could theoretically hide read errors or partial reads
No bounds changes: buffer size and loop logic remain the same
No input validation changes
No explicit security fix language in commit message or diff
Evidence from the diff
In main/usbhmsc/usbmode.c, the usbmode_ota_worker() function is modified to use POSIX open()/read()/close() instead of stdio fopen()/fread()/fclose() when reading the OTA firmware image from USB mass storage. The buffer handling logic is otherwise preserved. The change removes stdio’s internal buffering layer, which can reduce stack/heap pressure and avoid potential buffering anomalies during large file reads on an embedded ESP32 device. Error handling is simplified: the previous code checked fclose() return value and logged on failure; the new code just closes the fd unconditionally. The loop termination condition is also slightly tightened (bytes_read <= 0 instead of !bytes_read), which is functionally equivalent for size_t vs ssize_t.
Changed components
main/usbhmsc/usbmode.cusbmode_ota_worker()USB MSC OTA firmware update pathInspect captured patch +6 / −5
diff --git a/main/usbhmsc/usbmode.c b/main/usbhmsc/usbmode.c
index 1ccee86..511e7c2 100644
--- a/main/usbhmsc/usbmode.c
+++ b/main/usbhmsc/usbmode.c
@@ -15,6 +15,7 @@
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
+#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
@@ -615,7 +616,7 @@ static void usbmode_ota_worker(void* ctx)
uint8_t buffer[JADE_OTA_BUF_SIZE];
size_t msgs_sent = 1; // Initially just an "ota" message sent
size_t msgs_waited = 0;
- FILE* fp = fopen(ctx_data->file_to_flash, "rb");
+ const int fd = open(ctx_data->file_to_flash, O_RDONLY, 0);
free(ctx_data->file_to_flash);
ctx_data->file_to_flash = NULL;
@@ -640,8 +641,8 @@ static void usbmode_ota_worker(void* ctx)
}
// Read, encode and send a chunk of data to the ota task
- const size_t bytes_read = fp ? fread(buffer, 1, sizeof(buffer), fp) : 0;
- if (!bytes_read) {
+ const ssize_t bytes_read = fd < 0 ? 0 : read(fd, buffer, sizeof(buffer));
+ if (bytes_read <= 0) {
// Failed to read from the USB storage file.
// e.g. the device was unplugged or is unreliable.
break;
@@ -652,8 +653,8 @@ static void usbmode_ota_worker(void* ctx)
ctx_data->data_to_send -= bytes_read;
}
- if (fp && !fclose(fp)) {
- JADE_LOGE("Closing file failed");
+ if (fd >= 0) {
+ close(fd);
}
/* const bool all_data_sent = ctx_data->data_to_send == 0; */
Why this scored 16/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.