Add targeted SQL failure injection to real signed feedback tests
This commit is contained in:
1 parent
1e5fc2d335
commit
6a4fa8065a
2 files changed
+75
-4
No files matched your search
@@ -896,6 +896,9 @@ test_merkle_sync_SOURCES = test_merkle_sync.c $(test_clock_sources)
|
||||
test_chat_feedback_SOURCES = test_chat_feedback.c
|
||||
test_chat_feedback_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/lib
|
||||
test_chat_feedback_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
test_chat_feedback_LDFLAGS = -Wl,--wrap=sqlite3_exec -Wl,--wrap=sqlite3_prepare_v2 -Wl,--wrap=sqlite3_bind_blob \
|
||||
-Wl,--wrap=sqlite3_bind_int64 -Wl,--wrap=sqlite3_bind_int -Wl,--wrap=sqlite3_step -Wl,--wrap=sqlite3_changes \
|
||||
-Wl,--wrap=sqlite3_finalize
|
||||
test_merkle_protocol_SOURCES = test_merkle_protocol.c
|
||||
test_merkle_protocol_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/chat -I$(top_srcdir)/lib
|
||||
test_merkle_protocol_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
@@ -5,6 +5,74 @@
|
||||
static const uint8_t owner_private[32] = {9};
|
||||
static int events;
|
||||
|
||||
enum sql_fault { F_NONE, F_BEGIN, F_PREPARE, F_BIND, F_STEP, F_CHANGES, F_FINALIZE, F_COMMIT };
|
||||
enum sql_operation { F_GET, F_PUT };
|
||||
static enum sql_fault injected;
|
||||
static enum sql_operation injected_operation;
|
||||
static int injected_bind, failed_rollback, last_put;
|
||||
|
||||
static int fault_statement(const char* sql) {
|
||||
const char* prefix = injected_operation == F_GET ? "SELECT message_id,node_id,kind,ver,data,signature FROM feedback_1001 " :
|
||||
"INSERT INTO feedback_1001(record_key,bucket,message_id,node_id,kind,ver,data,signature)";
|
||||
return sql && !strncmp(sql, prefix, strlen(prefix));
|
||||
}
|
||||
|
||||
static int take_fault(enum sql_fault point, const char* sql, int index) {
|
||||
if (injected != point || (sql && !fault_statement(sql)) || (point == F_BIND && index != injected_bind)) return 0;
|
||||
injected = F_NONE;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "feedback test: injected stage=%d operation=%d bind=%d", point, injected_operation, 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 ((!strcmp(sql, "SAVEPOINT feedback_update") || strstr(sql, "BEGIN IMMEDIATE /* feedback")) && take_fault(F_BEGIN, NULL, 0))
|
||||
return SQLITE_BUSY;
|
||||
if ((!strcmp(sql, "RELEASE feedback_update") || !strncmp(sql, "COMMIT /* feedback", 18)) && take_fault(F_COMMIT, NULL, 0))
|
||||
return SQLITE_BUSY;
|
||||
if (failed_rollback && !strncmp(sql, "ROLLBACK", 8)) {
|
||||
failed_rollback = 0; DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "feedback test: injected rollback failure"); 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_fault(F_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* bytes, int length, void (*destroy)(void*)) {
|
||||
if (take_fault(F_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM;
|
||||
return __real_sqlite3_bind_blob(st, index, bytes, 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_fault(F_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM;
|
||||
return __real_sqlite3_bind_int64(st, index, value);
|
||||
}
|
||||
extern int __real_sqlite3_bind_int(sqlite3_stmt*, int, int);
|
||||
int __wrap_sqlite3_bind_int(sqlite3_stmt* st, int index, int value) {
|
||||
if (take_fault(F_BIND, sqlite3_sql(st), index)) return SQLITE_NOMEM;
|
||||
return __real_sqlite3_bind_int(st, index, value);
|
||||
}
|
||||
extern int __real_sqlite3_step(sqlite3_stmt*);
|
||||
int __wrap_sqlite3_step(sqlite3_stmt* st) {
|
||||
const char* sql = sqlite3_sql(st); last_put = !strncmp(sql, "INSERT INTO feedback_1001(", 26);
|
||||
if (take_fault(F_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_put && take_fault(F_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_fault(F_FINALIZE, sqlite3_sql(st), 0);
|
||||
int rc = __real_sqlite3_finalize(st); return fail ? SQLITE_IOERR : rc;
|
||||
}
|
||||
|
||||
/* Событие должно видеть опубликованную транзакцию. */
|
||||
static void on_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* data, int len) {
|
||||
(void)data; (void)len;
|
||||
@@ -17,13 +85,13 @@ static void public_key(const uint8_t priv[32], uint8_t pub[32]) {
|
||||
}
|
||||
|
||||
/* Экземпляры используют один uasync данного потока, но разные БД и идентичности. */
|
||||
static void setup(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
|
||||
static void setup(struct UTUN_INSTANCE* inst, struct UASYNC* ua, const char* path) {
|
||||
memset(inst, 0, sizeof(*inst)); inst->ua = ua;
|
||||
assert(sc_generate_keypair(&inst->my_keys) == SC_OK);
|
||||
inst->node_id = sc_derive_node_id_from_pubkey(inst->my_keys.public_key);
|
||||
memcpy(inst->my_ed25519_privkey, inst->my_keys.private_key, 32);
|
||||
public_key(inst->my_ed25519_privkey, inst->my_ed25519_pubkey);
|
||||
assert(sqlite3_open(":memory:", &inst->topo_sqlite_db) == SQLITE_OK);
|
||||
assert(sqlite3_open(path ? path : ":memory:", &inst->topo_sqlite_db) == SQLITE_OK);
|
||||
assert(topo_node_sqlite_init(inst->topo_sqlite_db) == 0);
|
||||
uint8_t x[32] = {3}, ed[32], zero[64] = {0}; public_key(owner_private, ed);
|
||||
assert(topo_node_sqlite_channel_put(inst->topo_sqlite_db, "1001", "one", 1, x, NULL, ed, NULL, zero) == 0);
|
||||
@@ -94,7 +162,7 @@ static int veto_commit(void* arg) { int* remaining = arg; return (*remaining)--
|
||||
|
||||
/* Исключение, purge и ожидание signer не уничтожают последние состояния и отмены. */
|
||||
static void membership_regressions(struct UASYNC* ua) {
|
||||
struct UTUN_INSTANCE author, target, inviter; setup(&author, ua); setup(&target, ua); setup(&inviter, ua);
|
||||
struct UTUN_INSTANCE author, target, inviter; setup(&author, ua, NULL); setup(&target, ua, NULL); setup(&inviter, ua, NULL);
|
||||
for (unsigned ch = 0; ch < 2; ch++) {
|
||||
const char* channel = ch ? "1002" : "1001";
|
||||
add_member(&author, &author, channel); add_member(&target, &author, channel); add_member(&target, &target, channel);
|
||||
@@ -271,7 +339,7 @@ int main(int argc, char** argv) {
|
||||
if (argc == 2) return check_profile_authors(argv[1]);
|
||||
size_t baseline = u_get_allocated_count();
|
||||
struct UASYNC* ua = uasync_create(); assert(ua);
|
||||
struct UTUN_INSTANCE source, target; setup(&source, ua); setup(&target, ua);
|
||||
struct UTUN_INSTANCE source, target; setup(&source, ua, NULL); setup(&target, ua, NULL);
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
const char* channel = ch ? "1002" : "1001";
|
||||
add_member(&source, &source, channel); add_member(&target, &source, channel);
|
||||
|
||||
Reference in new issue
Block a user