Reject old encrypted ACKs when compact attempt pairs restart in a new epoch

This commit is contained in:
evgeny committed 2026-10-23 19:27:06 +02:00
1 parent 7c09bfca7f
commit be95e93a36
1 file changed
+44
+44
View File
@@ -447,6 +447,49 @@ static void oom_session_recovery(void) {
puts("PASS encrypted OOM recovery: REINIT both owners, preserved links, stale DATA rejected, fresh delivery both ways");
}
/* Настоящий AES/ingress: одинаковая пара seq=1/no=1 в новой эпохе не принимает прежний ACK. */
static struct frame encrypted_attempt_ack(void) {
struct ETCP_DGRAM* pkt = memory_pool_alloc(ep[1].inst.pkt_pool); CHECK(pkt);
pkt->link = &ep[1].links[0]; pkt->data_len = etcp_ack_section_size(1); pkt->noencrypt_len = 0;
memset(pkt->data,0,pkt->data_len); pkt->data[0] = ETCP_SECTION_ACK; pkt->data[1] = 1;
pkt->data[ETCP_ACK_BASE_SIZE] = 1; pkt->data[ETCP_ACK_BASE_SIZE+4] = 1;
unsigned before = tail;
CHECK(etcp_encrypt_send(pkt) > 0 && tail == before+1 && head == before);
memory_pool_free(ep[1].inst.pkt_pool,pkt);
return frames[head++ % 4096];
}
static void compact_attempt_epoch_reuse(void) {
setup(0);
ep[0].inst.data_pool = memory_pool_init(PACKET_DATA_SIZE,"attempt epoch payload"); CHECK(ep[0].inst.data_pool);
struct frame old_ack = {0};
uint64_t first_epoch = ep[0].c->reset_id;
for (unsigned round = 0; round < 2; round++) {
struct ETCP_CONN* c = ep[0].c; struct ETCP_LINK* link = &ep[0].links[0];
link->inflight_lim_bytes = 65536;
const uint8_t data[] = {1,0,253};
CHECK(etcp_int_send(c,data,sizeof(data)) == 0);
struct ETCP_DGRAM* pkt = etcp_request_pkt(c); CHECK(pkt && pkt->attempt);
CHECK(pkt->attempt->seq == 1 && pkt->attempt->attempt_id == etcp_tx_attempt_id(1,1));
etcp_tx_complete(pkt,1); memory_pool_free(ep[0].inst.pkt_pool,pkt);
if (!round) {
old_ack = encrypted_attempt_ack();
etcp_conn_fatal_reinit(c,"attempt epoch reuse test"); converge();
CHECK(c->reset_id != first_epoch);
} else {
CHECK(c->unacked_bytes == sizeof(data) && link->inflight_bytes == sizeof(data));
uint64_t last_rx = link->last_recv_local_time;
deliver(&old_ack,0);
CHECK(c->unacked_bytes == sizeof(data) && link->inflight_bytes == sizeof(data));
CHECK(!c->ack_packets_count && !link->peer_rx_data_time_us && link->last_recv_local_time == last_rx);
struct frame fresh_ack = encrypted_attempt_ack(); deliver(&fresh_ack,0);
CHECK(!c->unacked_bytes && !link->inflight_bytes && c->ack_packets_count == 1);
}
}
teardown(); memory_pool_destroy(ep[0].inst.data_pool); ep[0].inst.data_pool = NULL;
puts("PASS encrypted ACK isolation when compact attempt pair is reused in a new epoch");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
test_clock_init(10);
@@ -463,6 +506,7 @@ int main(void) {
}
restart_during_confirmation(); CHECK(u_get_allocated_count() == baseline);
oom_session_recovery(); CHECK(u_get_allocated_count() == baseline);
compact_attempt_epoch_reuse(); CHECK(u_get_allocated_count() == baseline);
for (int scenario=0;scenario<9;scenario++) {
setup(scenario == 5);
saved[2]=stream_packet(0); saved[3]=stream_packet(1);