Instrument channel creation SQL and hash committed schema, identities and roots
This commit is contained in:
1 parent
60942e542b
commit
1d84765f08
2 files changed
+94
-1
No files matched your search
+3
-1
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user