Keep caller transactions intact on invalid receive commit and guard SQLite blob reads

This commit is contained in:
evgeny committed 2026-11-04 01:05:04 +01:00
1 parent 797e5d39c4
commit ddf570c791
2 files changed
+11 -2

No files matched your search

+8 -2
View File
@@ -1236,7 +1236,8 @@ static int member_read_saved(sqlite3* db, const char* ns, uint64_t node, int sta
uint8_t* blobs[] = {out->x, out->ed, out->join, out->update, out->owner, out->tree};
for (size_t i = 0; i < 6; i++) {
int bytes = sqlite3_column_bytes(st, cols[i]);
if (bytes == sizes[i]) memcpy(blobs[i], sqlite3_column_blob(st, cols[i]), (size_t)bytes);
const void* blob = sqlite3_column_blob(st, cols[i]);
if (bytes == sizes[i] && blob) memcpy(blobs[i], blob, (size_t)bytes);
else if (bytes || i < 2) out->malformed = 1;
}
for (int i = 0; i < 2; i++) {
@@ -1250,6 +1251,7 @@ static int member_read_saved(sqlite3* db, const char* ns, uint64_t node, int sta
.userinfo=out->info,.adm_tags=out->tags,.adm_tags_sig=out->owner,
.signed_by=(uint64_t)sqlite3_column_int64(st,9),.signature=out->tree};
out->source = sqlite3_column_int(st, 11);
if (sqlite3_errcode(db) == SQLITE_NOMEM) rc = SQLITE_NOMEM;
}
int final = sqlite3_finalize(st);
if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) {
@@ -1403,7 +1405,11 @@ static int member_commit_pull(void* ctx, void* state) {
struct UTUN_INSTANCE* inst = ctx; struct member_pull* pull = state; sqlite3* db = _db(inst);
char sql[2048]; sqlite3_stmt* st = NULL; const char* stage = "begin";
uint64_t* incoming = NULL; size_t count = 0; int result = MT_ERR_IO, rc;
if (!sqlite3_get_autocommit(db) || sqlite3_exec(db, "BEGIN IMMEDIATE /* member receive */", NULL, NULL, NULL) != SQLITE_OK) goto fail;
if (!sqlite3_get_autocommit(db)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "member receive commit requires own transaction ch=%s sync=%lld",
pull->channel, (long long)pull->id); return MT_ERR_IO;
}
if (sqlite3_exec(db, "BEGIN IMMEDIATE /* member receive */", NULL, NULL, NULL) != SQLITE_OK) goto fail;
stage = "policy";
snprintf(sql, sizeof(sql), "SELECT sync_policy FROM peers_%s WHERE id=%lld AND record_state=1", pull->channel, (long long)pull->id);
rc = sqlite3_prepare_v2(db, sql, -1, &st, NULL);
+3
View File
@@ -104,6 +104,9 @@ int main(void) {
DEBUG_INFO(DEBUG_CATEGORY_MEMBER_SYNC, "PASS two peers: isolated staging, no early profile/hash change, stale completion preserves newer commit");
profile(&source, 2, 4); a = receive(&source, &target, 2, identities[1].id); b = receive(&source, &target, 2, identities[3].id);
assert(sqlite3_exec(target.topo_sqlite_db, "BEGIN", NULL, NULL, NULL) == SQLITE_OK);
assert(member_commit_pull(&target, a) == MT_ERR_IO && !sqlite3_get_autocommit(target.topo_sqlite_db));
assert(sqlite3_exec(target.topo_sqlite_db, "ROLLBACK", NULL, NULL, NULL) == SQLITE_OK);
member_abort_pull(&target, a);
assert(scalar(&target, "SELECT COUNT(*) FROM peers_1001 WHERE record_state=1") == 2);
finish_receive(&target, b); assert(version(&target) == 4);