fix: enhance scratchpad safety and optimize memory handling

- Strengthened scratchpad bounds validation and ensured pointer range checks.
- Improved memory management with fallback allocation and robust cleanup.
- Adjusted SEADER_UART_RX_BUF_SIZE to 300 for better headroom.
- Added safeguards against frame/header underflows in scratchpad handling.
- Ensured proper alignment for UART worker and zero-copy transmission paths.
This commit is contained in:
CinderSocket committed 2026-03-03 12:44:41 -08:00
1 parent 132dc1860a
commit 836f91fad7
5 files changed
+92 -18

No files matched your search

-2
View File
@@ -1,5 +1,3 @@
all: gitsub asn1 build
gitsub:
+20 -2
View File
@@ -158,16 +158,35 @@ void seader_ccid_XfrBlockToSlot(
uint8_t slot,
uint8_t* data,
size_t len) {
bool in_scratchpad = (data >= seader_uart->tx_buf && data < seader_uart->tx_buf + SEADER_UART_RX_BUF_SIZE);
uint8_t header_len = 2 + 10;
if(len > ((size_t)SEADER_UART_RX_BUF_SIZE - header_len)) {
FURI_LOG_E(TAG, "CCID frame too long: %d", (int)(header_len + len));
return;
}
uintptr_t tx_start = (uintptr_t)seader_uart->tx_buf;
uintptr_t tx_end = tx_start + SEADER_UART_RX_BUF_SIZE;
uintptr_t data_addr = (uintptr_t)data;
bool in_scratchpad = false;
if(data_addr >= tx_start + header_len && data_addr <= tx_end) {
size_t available = (size_t)(tx_end - data_addr);
in_scratchpad = len <= available;
}
uint8_t* frame;
if(in_scratchpad) {
frame = data - header_len;
seader_uart->tx_len = header_len + len;
// Shift frame to start of tx_buf for UART worker
if(frame != seader_uart->tx_buf) {
memmove(seader_uart->tx_buf, frame, seader_uart->tx_len);
frame = seader_uart->tx_buf;
}
} else {
frame = seader_uart->tx_buf;
memset(frame, 0, header_len);
memcpy(frame + header_len, data, len);
seader_uart->tx_len = header_len + len;
}
frame[0] = SYNC;
@@ -183,7 +202,6 @@ void seader_ccid_XfrBlockToSlot(
frame[2 + 8] = 0;
frame[2 + 9] = 0;
seader_uart->tx_len = header_len + len;
seader_uart->tx_len = seader_add_lrc(frame, seader_uart->tx_len);
/*
+69 -12
View File
@@ -185,16 +185,28 @@ bool seader_send_apdu(
bool extended = seader_uart->T == 1;
uint8_t header_len = extended ? 7 : 5;
if(header_len + payloadLen > SEADER_UART_RX_BUF_SIZE) {
// Must account for MAX_FRAME_HEADERS headroom in scratchpad mode
if(MAX_FRAME_HEADERS + header_len + payloadLen > SEADER_UART_RX_BUF_SIZE) {
FURI_LOG_E(TAG, "Cannot send message, too long: %d", header_len + payloadLen);
return false;
}
uint8_t length = header_len + payloadLen;
uint8_t* apdu;
bool must_free = false;
uintptr_t tx_start = (uintptr_t)seader_uart->tx_buf;
uintptr_t tx_end = tx_start + SEADER_UART_RX_BUF_SIZE;
uintptr_t payload_addr = (uintptr_t)payload;
bool scratchpad_payload = false;
if(in_scratchpad) {
apdu = payload - header_len;
// in_scratchpad is only valid when the full payload range is inside tx_buf.
if(in_scratchpad && payload_addr >= tx_start + header_len && payload_addr <= tx_end) {
size_t available = (size_t)(tx_end - payload_addr);
scratchpad_payload = payloadLen <= available;
}
if(scratchpad_payload) {
apdu = (uint8_t*)(payload_addr - header_len);
} else {
apdu = malloc(length);
if(!apdu) {
@@ -202,6 +214,7 @@ bool seader_send_apdu(
return false;
}
memcpy(apdu + header_len, payload, payloadLen);
must_free = true;
}
apdu[0] = CLA;
@@ -225,7 +238,7 @@ bool seader_send_apdu(
seader_ccid_XfrBlock(seader_uart, apdu, length);
}
if(!in_scratchpad) {
if(must_free) {
free(apdu);
}
@@ -257,9 +270,41 @@ void seader_send_payload(
uint8_t* scratchpad = seader_uart->tx_buf + MAX_FRAME_HEADERS;
size_t scratchpad_size = SEADER_UART_RX_BUF_SIZE - MAX_FRAME_HEADERS;
size_t max_der_len = UINT8_MAX - ASN1_PREFIX;
uint8_t* payload_buf = scratchpad;
bool payload_in_scratchpad = true;
asn_enc_rval_t er =
der_encode_to_buffer(&asn_DEF_Payload, payload, scratchpad + ASN1_PREFIX, scratchpad_size - ASN1_PREFIX);
der_encode_to_buffer(
&asn_DEF_Payload, payload, scratchpad + ASN1_PREFIX, scratchpad_size - ASN1_PREFIX);
if(er.encoded < 0 || ((size_t)er.encoded + ASN1_PREFIX) > UINT8_MAX) {
payload_buf = malloc(ASN1_PREFIX + max_der_len);
if(!payload_buf) {
FURI_LOG_E(TAG, "Failed to allocate DER fallback buffer");
return;
}
payload_in_scratchpad = false;
er = der_encode_to_buffer(&asn_DEF_Payload, payload, payload_buf + ASN1_PREFIX, max_der_len);
}
if(er.encoded < 0) {
FURI_LOG_E(TAG, "Failed to encode payload");
if(!payload_in_scratchpad) {
free(payload_buf);
}
return;
}
size_t apdu_payload_len = ASN1_PREFIX + (size_t)er.encoded;
if(apdu_payload_len > UINT8_MAX) {
FURI_LOG_E(TAG, "Encoded payload too large for APDU: %d", (int)apdu_payload_len);
if(!payload_in_scratchpad) {
free(payload_buf);
}
return;
}
#ifdef ASN1_DEBUG
if(er.encoded > -1) {
@@ -277,14 +322,26 @@ void seader_send_payload(
#endif
//0xa0, 0xda, 0x02, 0x63, 0x00, 0x00, 0x0a,
//0x44, 0x0a, 0x44, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x96, 0x00
scratchpad[0] = from;
scratchpad[1] = to;
scratchpad[2] = replyTo;
scratchpad[3] = 0x00;
scratchpad[4] = 0x00;
scratchpad[5] = 0x00;
payload_buf[0] = from;
payload_buf[1] = to;
payload_buf[2] = replyTo;
payload_buf[3] = 0x00;
payload_buf[4] = 0x00;
payload_buf[5] = 0x00;
seader_send_apdu(seader, 0xA0, 0xDA, 0x02, 0x63, scratchpad, 6 + er.encoded, true);
seader_send_apdu(
seader,
0xA0,
0xDA,
0x02,
0x63,
payload_buf,
(uint8_t)apdu_payload_len,
payload_in_scratchpad);
if(!payload_in_scratchpad) {
free(payload_buf);
}
}
void seader_send_process_config_card(Seader* seader) {
+1 -1
View File
@@ -10,7 +10,7 @@
#include <furi_hal.h>
// https://ww1.microchip.com/downloads/en/DeviceDoc/00001561C.pdf
#define SEADER_UART_RX_BUF_SIZE (261)
#define SEADER_UART_RX_BUF_SIZE (300)
typedef struct {
uint8_t uart_ch;
+2 -1
View File
@@ -125,7 +125,8 @@ void seader_send_t1_scratchpad(SeaderUartBridge* seader_uart, uint8_t PCB, uint8
void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len) {
uint8_t ifsc = seader_uart->IFSC;
bool in_scratchpad = (apdu >= seader_uart->tx_buf && apdu < seader_uart->tx_buf + SEADER_UART_RX_BUF_SIZE);
bool in_scratchpad = (apdu >= seader_uart->tx_buf + 3 &&
apdu < seader_uart->tx_buf + SEADER_UART_RX_BUF_SIZE);
if(len > ifsc) {
if(seader_t_1_tx_buffer == NULL) {