From 96a3d05c7423e4459a7af9641757fea2a7bbf16e Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 03:37:29 +0100 Subject: [PATCH] Reject failed feedback page and hash reads and report transaction error codes --- src/chat/chat_feedback.c | 48 +++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/chat/chat_feedback.c b/src/chat/chat_feedback.c index a4e87ea8..26972fad 100644 --- a/src/chat/chat_feedback.c +++ b/src/chat/chat_feedback.c @@ -258,13 +258,17 @@ static void fb_committed(struct UTUN_INSTANCE* inst, const char* channel, const static int fb_finish_transaction(struct UTUN_INSTANCE* inst, const char* channel, int result) { sqlite3* db = inst->topo_sqlite_db; if (result >= 0) { - if (sqlite3_exec(db, "COMMIT /* feedback update */", NULL, NULL, NULL) == SQLITE_OK) return result; - fb_db_error(inst, channel, "commit"); + int rc = sqlite3_exec(db, "COMMIT /* feedback update */", NULL, NULL, NULL); + if (rc == SQLITE_OK) return result; + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: commit failed ch=%s rc=%d (%s) db=%s", channel, rc, sqlite3_errstr(rc), sqlite3_errmsg(db)); } - if (!sqlite3_get_autocommit(db) && sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL) != SQLITE_OK) - fb_db_error(inst, channel, "rollback"); if (!sqlite3_get_autocommit(db)) { - DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: transaction remains open ch=%s; owner must stop core and close DB after callback", channel); + int rc = sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL); + if (rc != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: rollback failed ch=%s rc=%d (%s) db=%s", + channel, rc, sqlite3_errstr(rc), sqlite3_errmsg(db)); + } + if (!sqlite3_get_autocommit(db)) { + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: transaction remains open ch=%s; requesting core stop and owner DB close after callback", channel); utun_instance_stop(inst); } return -1; @@ -332,16 +336,22 @@ finish: static int fb_page(void* ctx, const char* channel, uint64_t prefix, int av, const merkle_key_t* after, uint8_t* data, size_t* len, merkle_key_t* next, int* more) { struct UTUN_INSTANCE* inst = ctx; char table[40], sql[384]; uint64_t id; - if (fb_table(inst, channel, table, &id) < 0 || *len < 2) { + if (fb_table(inst, channel, table, &id) < 0 || !len || *len < 2 || !after || !data || !next || !more) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: invalid page capacity"); return -1; } merkle_key_t cursor = *after; memset(next, 0, sizeof(*next)); *more = 0; snprintf(sql, sizeof(sql), "SELECT message_id,node_id,kind,ver,data,signature,record_key,bucket FROM %s" " WHERE bucket=? AND (?=0 OR record_key>?) ORDER BY record_key", table); sqlite3_stmt* st = NULL; - if (sqlite3_prepare_v2(inst->topo_sqlite_db, sql, -1, &st, NULL) != SQLITE_OK) return fb_db_error(inst, channel, "page"); - sqlite3_bind_int64(st, 1, (sqlite3_int64)(prefix >> 39)); sqlite3_bind_int(st, 2, av); - sqlite3_bind_blob(st, 3, cursor.bytes, 32, SQLITE_STATIC); + int rc = sqlite3_prepare_v2(inst->topo_sqlite_db, sql, -1, &st, NULL); + if (rc == SQLITE_OK) rc = sqlite3_bind_int64(st, 1, (sqlite3_int64)(prefix >> 39)); + if (rc == SQLITE_OK) rc = sqlite3_bind_int(st, 2, av); + if (rc == SQLITE_OK) rc = sqlite3_bind_blob(st, 3, cursor.bytes, 32, SQLITE_STATIC); + if (rc != SQLITE_OK) { + int final = sqlite3_finalize(st); + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: page query failed ch=%s rc=%d finalize=%d db=%s", + channel, rc, final, sqlite3_errmsg(inst->topo_sqlite_db)); return -1; + } size_t off = 2; unsigned count = 0; int step, result = 0; while ((step = sqlite3_step(st)) == SQLITE_ROW) { struct chat_feedback_record r; uint8_t wire[FB_WIRE_MAX]; @@ -358,7 +368,11 @@ static int fb_page(void* ctx, const char* channel, uint64_t prefix, int av, cons memcpy(next->bytes, sqlite3_column_blob(st, 6), 32); } if (result == 0 && !*more && step != SQLITE_DONE) result = fb_db_error(inst, channel, "page step"); - sqlite3_finalize(st); data[0] = count >> 8; data[1] = count & 255; *len = off; return result; + int final = sqlite3_finalize(st); + if (final != SQLITE_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: page finalize failed ch=%s rc=%d", channel, final); result = -1; + } + data[0] = count >> 8; data[1] = count & 255; *len = off; return result; } /* Хеширует подписанные тела: разные валидные подписи одного тела не меняют корень. */ @@ -370,8 +384,13 @@ static int fb_hash(void* ctx, const char* channel, uint8_t level, uint64_t prefi snprintf(sql, sizeof(sql), "SELECT message_id,node_id,kind,ver,data,signature,record_key,bucket" " FROM %s WHERE bucket=? ORDER BY record_key", table); sqlite3_stmt* st = NULL; - if (sqlite3_prepare_v2(inst->topo_sqlite_db, sql, -1, &st, NULL) != SQLITE_OK) return fb_db_error(inst, channel, "hash"); - sqlite3_bind_int64(st, 1, (sqlite3_int64)(prefix >> 39)); + int rc = sqlite3_prepare_v2(inst->topo_sqlite_db, sql, -1, &st, NULL); + if (rc == SQLITE_OK) rc = sqlite3_bind_int64(st, 1, (sqlite3_int64)(prefix >> 39)); + if (rc != SQLITE_OK) { + int final = sqlite3_finalize(st); + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: hash query failed ch=%s rc=%d finalize=%d db=%s", + channel, rc, final, sqlite3_errmsg(inst->topo_sqlite_db)); return -1; + } int step, count = 0; while ((step = sqlite3_step(st)) == SQLITE_ROW) { struct chat_feedback_record r; uint8_t wire[FB_WIRE_MAX]; @@ -384,7 +403,10 @@ static int fb_hash(void* ctx, const char* channel, uint8_t level, uint64_t prefi } count++; } - sqlite3_finalize(st); + int final = sqlite3_finalize(st); + if (final != SQLITE_OK) { + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "feedback: hash finalize failed ch=%s rc=%d", channel, final); return -1; + } if (count >= 0 && step != SQLITE_DONE) return fb_db_error(inst, channel, "hash step"); return count; }