From 1d84765f08f2083a6735535c18ddb3df57774044 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 04:40:36 +0100 Subject: [PATCH] Instrument channel creation SQL and hash committed schema, identities and roots --- tests/Makefile.am | 4 +- tests/test_chat_channel.c | 91 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index 26ecc2db..262d3c17 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -314,7 +314,9 @@ test_chat_setting_LDADD = $(LIBUTUN) $(COMMON_LIBS) test_chat_setting_LDFLAGS = -Wl,--wrap=chat_local_apply_settings test_chat_channel_SOURCES = test_chat_channel.c test_chat_channel_LDADD = $(LIBUTUN) $(COMMON_LIBS) -test_chat_channel_LDFLAGS = -Wl,--wrap=EVP_DigestSign -Wl,--wrap=sqlite3_bind_blob +test_chat_channel_LDFLAGS = -Wl,--wrap=EVP_DigestSign -Wl,--wrap=sqlite3_exec -Wl,--wrap=sqlite3_prepare_v2 \ + -Wl,--wrap=sqlite3_bind_blob -Wl,--wrap=sqlite3_bind_text -Wl,--wrap=sqlite3_bind_int64 -Wl,--wrap=sqlite3_bind_null \ + -Wl,--wrap=sqlite3_step -Wl,--wrap=sqlite3_changes -Wl,--wrap=sqlite3_finalize -Wl,--wrap=u_malloc_impl else test_socket_monitor_LDFLAGS += -Wl,--wrap=read endif diff --git a/tests/test_chat_channel.c b/tests/test_chat_channel.c index 46ed5df8..6a7b42d0 100644 --- a/tests/test_chat_channel.c +++ b/tests/test_chat_channel.c @@ -9,6 +9,7 @@ #include "../src/chat/chat_event.h" #include "../src/chat/member_sync.h" #include "../src/routing_layer/topo_node_sqlite.h" +#include "../src/routing_layer/topo_group.h" #include "../lib/platform_compat.h" #include "../lib/debug_config.h" #include "../lib/mem.h" @@ -17,6 +18,33 @@ static unsigned fail_sign_number, channel_events; static struct UTUN_INSTANCE* active; static sqlite3* reader; static int fail_profile_binding; +enum sql_fault { Q_NONE, Q_PREPARE, Q_BIND, Q_STEP, Q_CHANGES, Q_FINALIZE, Q_EXEC, Q_ALLOCATE }; +static enum sql_fault sql_fault; +static const char *sql_prefix, *sql_suffix; +static int sql_bind, allocations_until_failure, failed_rollback, last_target; + +static int sql_target(const char* sql) { + return sql_prefix && !strncmp(sql, sql_prefix, strlen(sql_prefix)) && (!sql_suffix || strstr(sql, sql_suffix)); +} +static int take_sql(enum sql_fault point, const char* sql, int index) { + if (sql_fault != point || (sql && !sql_target(sql)) || (point == Q_BIND && index != sql_bind)) return 0; + sql_fault = Q_NONE; + DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "channel test: SQL fault stage=%d bind=%d", point, index); return 1; +} + +extern int __real_sqlite3_exec(sqlite3*, const char*, int (*)(void*, int, char**, char**), void*, char**); +int __wrap_sqlite3_exec(sqlite3* db, const char* sql, int (*cb)(void*, int, char**, char**), void* arg, char** error) { + if (failed_rollback && !strcmp(sql, "ROLLBACK")) { + failed_rollback = 0; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "channel test: rollback failed"); return SQLITE_IOERR; + } + if (take_sql(Q_EXEC, sql, 0)) return SQLITE_IOERR; + return __real_sqlite3_exec(db, sql, cb, arg, error); +} +extern int __real_sqlite3_prepare_v2(sqlite3*, const char*, int, sqlite3_stmt**, const char**); +int __wrap_sqlite3_prepare_v2(sqlite3* db, const char* sql, int length, sqlite3_stmt** st, const char** tail) { + if (take_sql(Q_PREPARE, sql, 0)) { *st = NULL; return SQLITE_NOMEM; } + return __real_sqlite3_prepare_v2(db, sql, length, st, tail); +} extern int __real_sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int, void (*)(void*)); int __wrap_sqlite3_bind_blob(sqlite3_stmt* st, int index, const void* value, int length, void (*destroy)(void*)) { @@ -25,8 +53,71 @@ int __wrap_sqlite3_bind_blob(sqlite3_stmt* st, int index, const void* value, int fail_profile_binding = 0; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "channel test: injected root profile signature bind failure"); return SQLITE_NOMEM; } + if (take_sql(Q_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM; return __real_sqlite3_bind_blob(st, index, value, length, destroy); } +extern int __real_sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void (*)(void*)); +int __wrap_sqlite3_bind_text(sqlite3_stmt* st, int index, const char* value, int length, void (*destroy)(void*)) { + if (take_sql(Q_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM; + return __real_sqlite3_bind_text(st, index, value, length, destroy); +} +extern int __real_sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +int __wrap_sqlite3_bind_int64(sqlite3_stmt* st, int index, sqlite3_int64 value) { + if (take_sql(Q_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM; + return __real_sqlite3_bind_int64(st, index, value); +} +extern int __real_sqlite3_bind_null(sqlite3_stmt*, int); +int __wrap_sqlite3_bind_null(sqlite3_stmt* st, int index) { + if (take_sql(Q_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM; + return __real_sqlite3_bind_null(st, index); +} +extern int __real_sqlite3_step(sqlite3_stmt*); +int __wrap_sqlite3_step(sqlite3_stmt* st) { + const char* sql = sqlite3_sql(st); last_target = sql_target(sql); + if (take_sql(Q_STEP, sql, 0)) return SQLITE_IOERR; + return __real_sqlite3_step(st); +} +extern int __real_sqlite3_changes(sqlite3*); +int __wrap_sqlite3_changes(sqlite3* db) { + if (last_target && take_sql(Q_CHANGES, NULL, 0)) return 0; + return __real_sqlite3_changes(db); +} +extern int __real_sqlite3_finalize(sqlite3_stmt*); +int __wrap_sqlite3_finalize(sqlite3_stmt* st) { + int fail = st && take_sql(Q_FINALIZE, sqlite3_sql(st), 0); + int rc = __real_sqlite3_finalize(st); return fail ? SQLITE_IOERR : rc; +} +extern void* __real_u_malloc_impl(uint32_t, const char*); +void* __wrap_u_malloc_impl(uint32_t size, const char* location) { + if (sql_fault == Q_ALLOCATE && strstr(location, "topo_node_sqlite.c") && !--allocations_until_failure) { + sql_fault = Q_NONE; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "channel test: schema allocation failed bytes=%u", size); return NULL; + } + return __real_u_malloc_impl(size, location); +} + +/* Проверяем логические bytes описаний,всей схемы и Merkle через независимое соединение. */ +static void snapshot(sqlite3* db, uint8_t out[32]) { + const char* queries[] = { + "SELECT channel_id,name,owner_node_id,x25519_pubkey,x25519_privkey,ed25519_pubkey,ed25519_privkey,signature FROM channels ORDER BY channel_id", + "SELECT namespace,level,prefix64,hash FROM merkle_tree_hash ORDER BY namespace,level,prefix64", + "SELECT type,name,tbl_name,sql FROM sqlite_master ORDER BY type,name" + }; + EVP_MD_CTX* hash = EVP_MD_CTX_new(); assert(hash && EVP_DigestInit_ex(hash, EVP_sha256(), NULL) == 1); + for (unsigned query = 0; query < sizeof(queries) / sizeof(queries[0]); query++) { + assert(EVP_DigestUpdate(hash, &query, sizeof(query)) == 1); sqlite3_stmt* st = NULL; + assert(sqlite3_prepare_v2(db, queries[query], -1, &st, NULL) == SQLITE_OK); int rc; + while ((rc = sqlite3_step(st)) == SQLITE_ROW) { + for (int column = 0; column < sqlite3_column_count(st); column++) { + int type = sqlite3_column_type(st, column), bytes = sqlite3_column_bytes(st, column); + const void* value = sqlite3_column_blob(st, column); assert(!bytes || value); + assert(EVP_DigestUpdate(hash, &type, sizeof(type)) == 1 && EVP_DigestUpdate(hash, &bytes, sizeof(bytes)) == 1); + if (bytes) assert(EVP_DigestUpdate(hash, value, (size_t)bytes) == 1); + } + } + assert(rc == SQLITE_DONE && sqlite3_finalize(st) == SQLITE_OK); + } + unsigned length; assert(EVP_DigestFinal_ex(hash, out, &length) == 1 && length == 32); EVP_MD_CTX_free(hash); +} extern int __real_EVP_DigestSign(EVP_MD_CTX*, unsigned char*, size_t*, const unsigned char*, size_t); int __wrap_EVP_DigestSign(EVP_MD_CTX* ctx, unsigned char* signature, size_t* length, const unsigned char* data, size_t size) {