Cover feedback page and hash SQL errors without empty success

This commit is contained in:
evgeny committed 2026-10-27 03:38:21 +01:00
1 parent 96a3d05c74
commit d89a86c9dd
1 file changed
+35 -3
+35 -3
View File
@@ -6,12 +6,17 @@ 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 };
enum sql_operation { F_GET, F_PUT, F_PAGE, F_HASH };
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) {
if (injected_operation == F_PAGE || injected_operation == F_HASH) {
const char* prefix = "SELECT message_id,node_id,kind,ver,data,signature,record_key,bucket FROM feedback_1001 WHERE bucket=?";
return sql && !strncmp(sql, prefix, strlen(prefix)) &&
((strstr(sql, " AND (?=0 OR record_key>?)") != NULL) == (injected_operation == F_PAGE));
}
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));
@@ -58,7 +63,8 @@ int __wrap_sqlite3_bind_int(sqlite3_stmt* st, int index, int 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);
const char* sql = sqlite3_sql(st); const char* put = "INSERT INTO feedback_1001(";
last_put = !strncmp(sql, put, strlen(put));
if (take_fault(F_STEP, sql, 0)) return SQLITE_IOERR;
return __real_sqlite3_step(st);
}
@@ -324,6 +330,18 @@ static void fault_event(struct UTUN_INSTANCE* inst, int type, const uint8_t* dat
assert(!merkle_tree_get(feedback_reader->topo_sqlite_db, "1:1001", 0, 0, reader) && !memcmp(reader, writer, 32)); events++;
}
/* Те же операции, которыми Merkle получает лист и пересчитывает его хеш. */
static int feedback_read(struct UTUN_INSTANCE* inst, const struct chat_feedback_record* record, enum sql_operation operation) {
merkle_key_t key, after = {{0}}, next; assert(!fb_key(1001, record, &key));
uint64_t prefix = merkle_sync_level_prefix(fb_read64(key.bytes), MT_MAX_LEVEL);
if (operation == F_PAGE) {
uint8_t bytes[FB_WIRE_MAX + 2]; size_t length = sizeof(bytes); int more;
return fb_page(inst, "1001", prefix, 0, &after, bytes, &length, &next, &more);
}
EVP_MD_CTX* hash = EVP_MD_CTX_new(); assert(hash && EVP_DigestInit_ex(hash, EVP_sha256(), NULL) == 1);
int rc = fb_hash(inst, "1001", MT_MAX_LEVEL, prefix, hash); EVP_MD_CTX_free(hash); return rc;
}
static void sql_fault_regressions(struct UASYNC* ua) {
char path[] = "/tmp/utun-feedback-fault-XXXXXX"; int fd = mkstemp(path); assert(fd >= 0); close(fd);
struct UTUN_INSTANCE author, target, reader; memset(&reader, 0, sizeof(reader));
@@ -361,6 +379,20 @@ static void sql_fault_regressions(struct UASYNC* ua) {
}
}
}
unsigned reads = 0;
for (enum sql_operation operation = F_PAGE; operation <= F_HASH; operation++) {
for (enum sql_fault point = F_PREPARE; point <= F_FINALIZE; point++) {
if (point == F_CHANGES) continue;
int bindings = point == F_BIND ? (operation == F_PAGE ? 3 : 1) : 1;
for (int bind = 1; bind <= bindings; bind++) {
root(&target, before); saved = events;
injected_operation = operation; injected_bind = bind; injected = point;
assert(feedback_read(&target, &current, operation) < 0 && injected == F_NONE && events == saved);
assert_stored(&reader, &current); root(&target, after); assert(!memcmp(before, after, 32));
assert(feedback_read(&target, &current, operation) >= 0); reads++;
}
}
}
const char* triggers[] = {
"CREATE TEMP TRIGGER reject_feedback BEFORE INSERT ON feedback_1001 BEGIN SELECT RAISE(ABORT,'feedback'); END",
"CREATE TEMP TRIGGER reject_feedback BEFORE INSERT ON feedback_1001 BEGIN SELECT RAISE(IGNORE); END"
@@ -398,7 +430,7 @@ static void sql_fault_regressions(struct UASYNC* ua) {
member_sync_destroy(&target); assert(sqlite3_close(target.topo_sqlite_db) == SQLITE_OK); assert_stored(&reader, &current);
member_sync_destroy(&author); assert(sqlite3_close(author.topo_sqlite_db) == SQLITE_OK);
assert(sqlite3_close(reader.topo_sqlite_db) == SQLITE_OK); assert(!remove(path)); feedback_reader = NULL;
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS feedback:%u SQL faults/retries, stale-state lookup failure, ABORT/IGNORE/veto, foreign no-op/page, failed rollback stops owner; reader sees exact committed records/roots before events", cases);
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "PASS feedback:%u write/%u read faults and retries, stale lookup, ABORT/IGNORE/veto, foreign no-op/page, failed rollback; reader checks records/roots before events", cases, reads);
}
/* Диагностика копии профиля: без изменения записей или доступа к приватным ключам. */