fix(companion): signing takes the buffer it can get (#38)

CMD_SIGN_START promised the app 8K before a malloc it never checked. On
the L1 the nRF52 heap has ~5K free from boot, so the malloc always failed
and every CMD_SIGN_DATA answered ERR_CODE_BAD_STATE. Now it allocates
first, halving down to 256 B, and reports the size it got; nothing at all
is an error on SIGN_START itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-28 23:34:29 +02:00
co-authored by Claude Opus 5.5
parent b355922d32
commit 0e1b3c9cfd
2 changed files with 18 additions and 8 deletions
+17 -8
View File
@@ -1810,6 +1810,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
clearPendingReqs(); clearPendingReqs();
next_ack_idx = 0; next_ack_idx = 0;
sign_data = NULL; sign_data = NULL;
sign_data_cap = 0;
dirty_contacts_expiry = 0; dirty_contacts_expiry = 0;
memset(advert_paths, 0, sizeof(advert_paths)); memset(advert_paths, 0, sizeof(advert_paths));
memset(send_scope.key, 0, sizeof(send_scope.key)); memset(send_scope.key, 0, sizeof(send_scope.key));
@@ -2819,19 +2820,27 @@ void MyMesh::handleCmdFrame(size_t len) {
writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx
} }
} else if (cmd_frame[0] == CMD_SIGN_START) { } else if (cmd_frame[0] == CMD_SIGN_START) {
out_frame[0] = RESP_CODE_SIGN_START;
out_frame[1] = 0; // reserved
uint32_t len = MAX_SIGN_DATA_LEN;
memcpy(&out_frame[2], &len, 4);
_serial->writeFrame(out_frame, 6);
if (sign_data) { if (sign_data) {
free(sign_data); free(sign_data);
} }
sign_data = (uint8_t *)malloc(MAX_SIGN_DATA_LEN); // A small MCU's heap (nRF52: ~70K, most of it taken at boot) may not have
// 8K in one block: take the biggest buffer we can get and tell the app its size,
// rather than promising 8K and failing every SIGN_DATA with BAD_STATE.
sign_data_len = 0; sign_data_len = 0;
sign_data_cap = MAX_SIGN_DATA_LEN;
while ((sign_data = (uint8_t *)malloc(sign_data_cap)) == NULL && sign_data_cap > 256) {
sign_data_cap /= 2;
}
if (sign_data == NULL) {
writeErrFrame(ERR_CODE_BAD_STATE);
} else {
out_frame[0] = RESP_CODE_SIGN_START;
out_frame[1] = 0; // reserved
memcpy(&out_frame[2], &sign_data_cap, 4);
_serial->writeFrame(out_frame, 6);
}
} else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) { } else if (cmd_frame[0] == CMD_SIGN_DATA && len > 1) {
if (sign_data == NULL || sign_data_len + (len - 1) > MAX_SIGN_DATA_LEN) { if (sign_data == NULL || sign_data_len + (len - 1) > sign_data_cap) {
writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long writeErrFrame(sign_data == NULL ? ERR_CODE_BAD_STATE : ERR_CODE_TABLE_FULL); // error: too long
} else { } else {
memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1); memcpy(&sign_data[sign_data_len], &cmd_frame[1], len - 1);
+1
View File
@@ -633,6 +633,7 @@ private:
uint8_t app_target_ver; uint8_t app_target_ver;
uint8_t *sign_data; uint8_t *sign_data;
uint32_t sign_data_len; uint32_t sign_data_len;
uint32_t sign_data_cap; // what the malloc got: at most MAX_SIGN_DATA_LEN
unsigned long dirty_contacts_expiry; unsigned long dirty_contacts_expiry;
unsigned long _bot_last_ch_reply_ms; unsigned long _bot_last_ch_reply_ms;
unsigned long _bot_last_room_reply_ms; unsigned long _bot_last_room_reply_ms;