From 19c3cc04344e5e8474c330b72cc3da5dbf93447c Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 14:10:28 +0200 Subject: [PATCH] Reject interrupted membership channel queries --- src/routing_layer/topo_node_sqlite.c | 35 +++++++++++++++++++++------- tests/test_node_snapshot.c | 10 +++++--- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/routing_layer/topo_node_sqlite.c b/src/routing_layer/topo_node_sqlite.c index 87692864..1421a6df 100644 --- a/src/routing_layer/topo_node_sqlite.c +++ b/src/routing_layer/topo_node_sqlite.c @@ -206,24 +206,35 @@ int topo_node_sqlite_member_exists(sqlite3* db, uint64_t node_id) { int topo_node_sqlite_get_member_channels(sqlite3* db, uint64_t node_id, uint64_t** out_ch_ids, int* out_count) { - if (!db || !out_ch_ids || !out_count) return -1; - *out_ch_ids = NULL; *out_count = 0; + if (out_ch_ids) *out_ch_ids = NULL; + if (out_count) *out_count = 0; + if (!db || !out_ch_ids || !out_count) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "member channels: invalid arguments"); return -1; } sqlite3_stmt* s = NULL; if (sqlite3_prepare_v2(db, "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'peers\\_%' ESCAPE '\\'", - -1, &s, NULL) != SQLITE_OK) return -1; + -1, &s, NULL) != SQLITE_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "member channels: enumerate tables: %s", sqlite3_errmsg(db)); return -1; + } uint64_t* ids = NULL; int count = 0, cap = 0; char sql[256]; - while (sqlite3_step(s) == SQLITE_ROW) { + int rc; + while ((rc = sqlite3_step(s)) == SQLITE_ROW) { const char* tbl = (const char*)sqlite3_column_text(s, 0); if (!tbl || strncmp(tbl, "peers_", 6) != 0) continue; snprintf(sql, sizeof(sql), "SELECT 1 FROM \"%s\" WHERE node_id=?", tbl); sqlite3_stmt* m = NULL; - if (sqlite3_prepare_v2(db, sql, -1, &m, NULL) == SQLITE_OK) { - sqlite3_bind_int64(m, 1, (sqlite3_int64)node_id); - int is_member = (sqlite3_step(m) == SQLITE_ROW); + int member_rc = sqlite3_prepare_v2(db, sql, -1, &m, NULL); + if (member_rc == SQLITE_OK) member_rc = sqlite3_bind_int64(m, 1, (sqlite3_int64)node_id); + if (member_rc == SQLITE_OK) member_rc = sqlite3_step(m); + if (member_rc != SQLITE_ROW && member_rc != SQLITE_DONE) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "member channels: table=%s node=%016llx rc=%d: %s", + tbl, (unsigned long long)node_id, member_rc, sqlite3_errmsg(db)); + sqlite3_finalize(m); u_free(ids); sqlite3_finalize(s); return -1; + } + { + int is_member = member_rc == SQLITE_ROW; sqlite3_finalize(m); if (is_member) { uint64_t ch = strtoull(tbl + 6, NULL, 10); @@ -231,7 +242,10 @@ int topo_node_sqlite_get_member_channels(sqlite3* db, uint64_t node_id, if (count == cap) { cap = cap ? cap * 2 : 8; uint64_t* tmp = u_realloc(ids, (size_t)cap * sizeof(uint64_t)); - if (!tmp) { u_free(ids); sqlite3_finalize(s); return -1; } + if (!tmp) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "member channels: allocation failed count=%d", count); + u_free(ids); sqlite3_finalize(s); return -1; + } ids = tmp; } ids[count++] = ch; @@ -239,6 +253,11 @@ int topo_node_sqlite_get_member_channels(sqlite3* db, uint64_t node_id, } } } + if (rc != SQLITE_DONE) { + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "member channels: enumeration failed node=%016llx rc=%d: %s", + (unsigned long long)node_id, rc, sqlite3_errmsg(db)); + u_free(ids); sqlite3_finalize(s); return -1; + } sqlite3_finalize(s); *out_ch_ids = ids; *out_count = count; return 0; diff --git a/tests/test_node_snapshot.c b/tests/test_node_snapshot.c index b6000e39..20c4496f 100644 --- a/tests/test_node_snapshot.c +++ b/tests/test_node_snapshot.c @@ -63,9 +63,10 @@ static void test_zero_port_schema(void) { sqlite3_close(db); } -/* Отказ INSERT/UPDATE не должен выглядеть успешным сохранением уже проверенного узла. */ +/* Прерывает VM SQLite для проверки незавершённого чтения списка. */ static int interrupt_sql(void* arg) { (void)arg; return 1; } +/* Отказ SQL не должен выглядеть успешным сохранением или чтением узла. */ static void test_verified_write(void) { sqlite3* db = NULL; uint8_t key[32] = {1}; assert(sqlite3_open(":memory:", &db) == SQLITE_OK && !topo_node_sqlite_init(db)); @@ -94,13 +95,16 @@ static void test_verified_write(void) { assert(!topo_node_sqlite_set_connected(db, "123", 7, 1)); uint64_t* ids = NULL; int count = 0; assert(!topo_node_sqlite_get_connected_peers(db, "123", &ids, &count) && count == 1 && ids[0] == 7); u_free(ids); - for (int query = 0; query < 4; query++) { + assert(!topo_node_sqlite_get_member_channels(db, 7, &ids, &count) && count == 2); + assert((ids[0] == 123 && ids[1] == 124) || (ids[0] == 124 && ids[1] == 123)); u_free(ids); + for (int query = 0; query < 5; query++) { ids = (void*)(uintptr_t)1; count = 99; sqlite3_progress_handler(db, 1, interrupt_sql, NULL); int rc = query == 0 ? topo_node_sqlite_get_connected_peers(db, "123", &ids, &count) : query == 1 ? topo_node_sqlite_get_supernode_peers(db, "123", &ids, &count) : query == 2 ? topo_node_sqlite_get_public_peers(db, "123", &ids, &count, 0) : - topo_node_sqlite_get_local_peers(db, "123", &ids, &count, 0); + query == 3 ? topo_node_sqlite_get_local_peers(db, "123", &ids, &count, 0) : + topo_node_sqlite_get_member_channels(db, 7, &ids, &count); sqlite3_progress_handler(db, 0, NULL, NULL); assert(rc < 0 && !ids && !count && !sqlite3_next_stmt(db, NULL)); }