mirror of
https://github.com/bettse/seader.git
synced 2026-07-15 15:29:05 +00:00
t1: surface chained response allocation failures
This commit is contained in:
@@ -47,6 +47,8 @@ const char* seader_hf_read_failure_reason_text(SeaderHfReadFailureReason reason)
|
||||
return "Read state error";
|
||||
case SeaderHfReadFailureReasonSamKeysMissing:
|
||||
return "SAM missing keys";
|
||||
case SeaderHfReadFailureReasonResourceExhausted:
|
||||
return "SAM exchange memory error";
|
||||
case SeaderHfReadFailureReasonNone:
|
||||
default:
|
||||
return "Read failed";
|
||||
|
||||
@@ -34,6 +34,7 @@ typedef enum {
|
||||
SeaderHfReadFailureReasonProtocolError,
|
||||
SeaderHfReadFailureReasonInternalState,
|
||||
SeaderHfReadFailureReasonSamKeysMissing,
|
||||
SeaderHfReadFailureReasonResourceExhausted,
|
||||
} SeaderHfReadFailureReason;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
typedef struct BitBuffer BitBuffer;
|
||||
|
||||
BitBuffer* bit_buffer_alloc(size_t capacity_bytes);
|
||||
void bit_buffer_test_fail_next_alloc(bool fail);
|
||||
void bit_buffer_free(BitBuffer* buf);
|
||||
void bit_buffer_reset(BitBuffer* buf);
|
||||
void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes);
|
||||
|
||||
@@ -9,7 +9,14 @@ struct BitBuffer {
|
||||
size_t capacity_bytes;
|
||||
};
|
||||
|
||||
static bool bit_buffer_fail_next_alloc = false;
|
||||
|
||||
BitBuffer* bit_buffer_alloc(size_t capacity_bytes) {
|
||||
if(bit_buffer_fail_next_alloc) {
|
||||
bit_buffer_fail_next_alloc = false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BitBuffer* buf = malloc(sizeof(BitBuffer));
|
||||
buf->data = calloc(1, capacity_bytes);
|
||||
buf->size_bits = 0;
|
||||
@@ -17,6 +24,10 @@ BitBuffer* bit_buffer_alloc(size_t capacity_bytes) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
void bit_buffer_test_fail_next_alloc(bool fail) {
|
||||
bit_buffer_fail_next_alloc = fail;
|
||||
}
|
||||
|
||||
void bit_buffer_free(BitBuffer* buf) {
|
||||
if(buf) {
|
||||
free(buf->data);
|
||||
|
||||
@@ -29,3 +29,19 @@ void seader_worker_send_version(Seader* seader) {
|
||||
(void)seader;
|
||||
g_t1_host_test_state.send_version_call_count++;
|
||||
}
|
||||
|
||||
void seader_abort_active_read_with_reason(
|
||||
Seader* seader,
|
||||
SeaderHfReadFailureReason reason,
|
||||
const char* detail) {
|
||||
g_t1_host_test_state.abort_call_count++;
|
||||
seader->hf_read_failure_reason = reason;
|
||||
if(detail && detail[0] != '\0') {
|
||||
strncpy(seader->read_error, detail, sizeof(seader->read_error) - 1U);
|
||||
} else {
|
||||
strncpy(
|
||||
seader->read_error,
|
||||
seader_hf_read_failure_reason_text(reason),
|
||||
sizeof(seader->read_error) - 1U);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "bit_buffer.h"
|
||||
#include "hf_read_lifecycle.h"
|
||||
#include "lrc.h"
|
||||
#include "t_1_logic.h"
|
||||
|
||||
@@ -46,6 +47,8 @@ struct SeaderWorker {
|
||||
|
||||
struct Seader {
|
||||
SeaderWorker* worker;
|
||||
SeaderHfReadFailureReason hf_read_failure_reason;
|
||||
char read_error[97];
|
||||
};
|
||||
|
||||
typedef struct CCID_Message {
|
||||
@@ -62,6 +65,10 @@ typedef struct CCID_Message {
|
||||
void seader_ccid_XfrBlock(SeaderUartBridge* seader_uart, uint8_t* data, size_t len);
|
||||
bool seader_worker_process_sam_message(Seader* seader, uint8_t* apdu, uint32_t len);
|
||||
void seader_worker_send_version(Seader* seader);
|
||||
void seader_abort_active_read_with_reason(
|
||||
Seader* seader,
|
||||
SeaderHfReadFailureReason reason,
|
||||
const char* detail);
|
||||
|
||||
typedef struct {
|
||||
/* Captured outbound CCID payload emitted by the T=1 implementation. */
|
||||
@@ -77,6 +84,7 @@ typedef struct {
|
||||
size_t send_version_call_count;
|
||||
size_t callback_call_count;
|
||||
uint32_t last_callback_event;
|
||||
size_t abort_call_count;
|
||||
} T1HostTestState;
|
||||
|
||||
extern T1HostTestState g_t1_host_test_state;
|
||||
|
||||
@@ -65,6 +65,9 @@ static MunitResult test_failure_reason_texts_are_stable(
|
||||
munit_assert_string_equal(
|
||||
seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonSamKeysMissing),
|
||||
"SAM missing keys");
|
||||
munit_assert_string_equal(
|
||||
seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonResourceExhausted),
|
||||
"SAM exchange memory error");
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
@@ -143,6 +146,10 @@ static MunitResult test_error_texts_fit_read_error_storage(
|
||||
munit_assert_size(strlen(protected_read_timeout), <, 96U);
|
||||
munit_assert_size(
|
||||
strlen(seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonInternalState)), <, 96U);
|
||||
munit_assert_size(
|
||||
strlen(seader_hf_read_failure_reason_text(SeaderHfReadFailureReasonResourceExhausted)),
|
||||
<,
|
||||
96U);
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -196,6 +196,35 @@ static MunitResult test_recv_i_block_too_large_rejected(const MunitParameter par
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
static MunitResult test_recv_chained_i_block_oom_returns_error(
|
||||
const MunitParameter params[],
|
||||
void* fixture) {
|
||||
(void)params;
|
||||
(void)fixture;
|
||||
|
||||
SeaderUartBridge uart = {0};
|
||||
SeaderWorker worker = {0};
|
||||
Seader seader = make_test_seader(&uart, &worker);
|
||||
uint8_t i_block_more[] = {0x00, 0x20, 0x02, 0xAA, 0xBB, 0x00};
|
||||
CCID_Message message = {.payload = i_block_more, .dwLength = sizeof(i_block_more)};
|
||||
|
||||
t1_host_test_reset();
|
||||
uart.t1.ifsd = SEADER_T1_IFS_DEFAULT;
|
||||
uart.t1.recv_pcb = 0x00;
|
||||
seader_add_lrc(i_block_more, sizeof(i_block_more) - 1U);
|
||||
bit_buffer_test_fail_next_alloc(true);
|
||||
|
||||
munit_assert_false(seader_recv_t1(&seader, &message));
|
||||
munit_assert_null(uart.t1.rx_buffer);
|
||||
munit_assert_size(g_t1_host_test_state.xfrblock_call_count, ==, 0);
|
||||
munit_assert_size(g_t1_host_test_state.process_call_count, ==, 0);
|
||||
munit_assert_size(g_t1_host_test_state.abort_call_count, ==, 1);
|
||||
munit_assert_int(
|
||||
seader.hf_read_failure_reason, ==, SeaderHfReadFailureReasonResourceExhausted);
|
||||
munit_assert_string_equal(seader.read_error, "SAM exchange memory error");
|
||||
return MUNIT_OK;
|
||||
}
|
||||
|
||||
static MunitResult test_recv_r_block_nack_retransmits(const MunitParameter params[], void* fixture) {
|
||||
(void)params;
|
||||
(void)fixture;
|
||||
@@ -393,6 +422,12 @@ static MunitTest test_t1_regression_cases[] = {
|
||||
NULL,
|
||||
MUNIT_TEST_OPTION_NONE,
|
||||
NULL},
|
||||
{(char*)"/recv/chained-i-block-oom-errors",
|
||||
test_recv_chained_i_block_oom_returns_error,
|
||||
NULL,
|
||||
NULL,
|
||||
MUNIT_TEST_OPTION_NONE,
|
||||
NULL},
|
||||
{(char*)"/recv/r-block-nack-retransmits",
|
||||
test_recv_r_block_nack_retransmits,
|
||||
NULL,
|
||||
|
||||
@@ -357,10 +357,6 @@ void* calloc(size_t count, size_t size) {
|
||||
|
||||
// Forward declarations
|
||||
static void seader_abort_active_read(Seader* seader);
|
||||
static void seader_abort_active_read_with_reason(
|
||||
Seader* seader,
|
||||
SeaderHfReadFailureReason reason,
|
||||
const char* detail);
|
||||
|
||||
static void seader_sam_set_state(
|
||||
Seader* seader,
|
||||
@@ -1038,7 +1034,7 @@ bool seader_parse_serial_number(Seader* seader, uint8_t* buf, size_t size) {
|
||||
return seader_sam_save_serial(seader, buf, size);
|
||||
}
|
||||
|
||||
static void seader_abort_active_read_with_reason(
|
||||
void seader_abort_active_read_with_reason(
|
||||
Seader* seader,
|
||||
SeaderHfReadFailureReason reason,
|
||||
const char* detail) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hf_bridge_policy.h"
|
||||
#include "hf_read_lifecycle.h"
|
||||
|
||||
typedef struct Seader Seader;
|
||||
typedef struct SeaderPollerContainer SeaderPollerContainer;
|
||||
@@ -41,6 +42,10 @@ void seader_send_no_card_detected(Seader* seader);
|
||||
bool seader_sam_can_accept_card(const Seader* seader);
|
||||
bool seader_sam_has_active_card(const Seader* seader);
|
||||
void seader_sam_force_idle_for_recovery(Seader* seader);
|
||||
void seader_abort_active_read_with_reason(
|
||||
Seader* seader,
|
||||
SeaderHfReadFailureReason reason,
|
||||
const char* detail);
|
||||
|
||||
bool seader_process_success_response_i(
|
||||
Seader* seader,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifdef SEADER_HOST_TEST
|
||||
#include "lib/host_tests/t_1_host_env.h"
|
||||
#else
|
||||
#include "sam_api.h"
|
||||
#include "t_1.h"
|
||||
#endif
|
||||
|
||||
@@ -279,6 +280,13 @@ bool seader_recv_t1(Seader* seader, CCID_Message* message) {
|
||||
seader_send_t1(seader_uart, NULL, 0);
|
||||
return false;
|
||||
|
||||
case SeaderT1ActionResourceExhausted:
|
||||
FURI_LOG_W(TAG, "T=1 chained APDU allocation failed");
|
||||
seader_t1_reset_link_state(t1);
|
||||
seader_abort_active_read_with_reason(
|
||||
seader, SeaderHfReadFailureReasonResourceExhausted, NULL);
|
||||
return false;
|
||||
|
||||
case SeaderT1ActionNone:
|
||||
return true;
|
||||
|
||||
|
||||
@@ -164,6 +164,9 @@ SeaderT1Action seader_t1_handle_block(
|
||||
t1->recv_pcb = seader_t1_next_pcb(t1->recv_pcb);
|
||||
if(t1->rx_buffer == NULL) {
|
||||
t1->rx_buffer = bit_buffer_alloc(512);
|
||||
if(t1->rx_buffer == NULL) {
|
||||
return SeaderT1ActionResourceExhausted;
|
||||
}
|
||||
}
|
||||
bit_buffer_append_bytes(t1->rx_buffer, payload + 3, LEN);
|
||||
return SeaderT1ActionSendAck;
|
||||
|
||||
@@ -29,6 +29,7 @@ typedef enum {
|
||||
SeaderT1ActionSendMoreData,
|
||||
SeaderT1ActionRetransmit,
|
||||
SeaderT1ActionSendNak,
|
||||
SeaderT1ActionResourceExhausted,
|
||||
SeaderT1ActionError,
|
||||
} SeaderT1Action;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user