fix(chat): validate owner versions and complete member reads before mutation

This commit is contained in:
evgeny committed 2026-10-27 07:02:44 +01:00
1 parent 91fb996a09
commit 16d7fc4fb5
2 files changed
+34 -7

No files matched your search

+30 -7
View File
@@ -52,6 +52,28 @@ static uint64_t member_read64(const uint8_t* p) {
return n;
}
/* Версия owner-блока — каноническая десятичная строка1..INT_MAX,без atoi overflow. */
static int owner_version_pair(const char* key, const char* text, void* arg) {
if (strcmp(key, "ver")) return 0;
int value = 0;
if (*text < '1' || *text > '9') goto invalid;
for (const char* p = text; *p; p++) {
if (*p < '0' || *p > '9' || value > (INT_MAX - (*p - '0')) / 10) goto invalid;
value = value * 10 + (*p - '0');
}
*(int*)arg = value; return 0;
invalid:
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "member owner: invalid version format/range"); return -1;
}
int member_sync_owner_version(sqlite3* db, const char* tags) {
if (!tags || !tags[0]) return 0;
int version = 0;
if (chat_json_strings(db, tags, MS_ADM_TAGS_MAX, owner_version_pair, &version)) return -1;
if (!version) { DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "member owner: missing version"); return -1; }
return version;
}
int member_sync_build_owner_msg(uint64_t node_id, const char* tags, uint8_t* out, size_t capacity) {
size_t len = tags ? strlen(tags) : 0;
if (!tags || !out || len > MS_ADM_TAGS_MAX || capacity < len + 8) {
@@ -577,10 +599,10 @@ static int member_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
if (sqlite3_errcode(db) == SQLITE_NOMEM) rc = SQLITE_NOMEM;
}
}
sqlite3_finalize(st);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: local record read failed ns=%s node=%016llx rc=%d: %s",
MS_ID, ch_id, (unsigned long long)m->node_id, rc, sqlite3_errmsg(db)); return MT_ERR_IO;
int final = sqlite3_finalize(st);
if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "%s: local record read failed ns=%s node=%016llx rc=%d finalize=%d: %s",
MS_ID, ch_id, (unsigned long long)m->node_id, rc, final, sqlite3_errmsg(db)); return MT_ERR_IO;
}
}
@@ -666,7 +688,8 @@ static int member_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
int adm_ver = 0, adm_storage = 0;
if (m->adm_tags && m->adm_tags[0] && m->adm_tags_sig && !_sig_is_zero64(m->adm_tags_sig)) {
char ver_str[32];
adm_ver = json_flat_get(m->adm_tags, "ver", ver_str, sizeof(ver_str)) == 0 ? atoi(ver_str) : 0;
adm_ver = member_sync_owner_version(db, m->adm_tags);
if (adm_ver < 0) return MT_ERR_IO;
adm_storage = (json_flat_get(m->adm_tags, "storage", ver_str, sizeof(ver_str)) == 0 && strcmp(ver_str, "yes") == 0) ? 1 : 0;
uint8_t ch_ed_pub[32] = {0};
int lookup = topo_node_sqlite_channel_get(db, ch_id, NULL, 0, NULL, NULL, ch_ed_pub, NULL);
@@ -689,8 +712,8 @@ static int member_apply_record(struct UTUN_INSTANCE* inst, const char* ch_id,
if (block_b_ok) {
int local_ver = 0;
if (has_local && local_tags[0]) {
char lv[32];
if (json_flat_get(local_tags, "ver", lv, sizeof(lv)) == 0) local_ver = atoi(lv);
local_ver = member_sync_owner_version(db, local_tags);
if (local_ver < 0) return MT_ERR_IO;
}
if (adm_ver > local_ver) block_b_changed = 1;
else if (adm_ver < local_ver) block_b_stale = 1;
+4
View File
@@ -56,6 +56,10 @@ int member_sync_verified_ed25519(struct UTUN_INSTANCE* inst, const char* channel
/* UTF-8 JSON bytes, excluding NUL. A complete record fits in one Merkle page. */
#define MS_ADM_TAGS_MAX 8192
#define MS_USERINFO_MAX 1024
/* Проверить весь строковый JSON owner-блока,уникальность ключей и ver1..INT_MAX без ведущих нулей.
* Возвращает версию,0 для NULL/пустого блока,-1 при ошибке формата/SQL. Подпись здесь не проверяется. */
int member_sync_owner_version(struct sqlite3* db, const char* tags);
/* Байты для подписи владельца: tags без NUL, затем node_id[8] в порядке байтов хоста.
* Возвращает длину или -1, если данные/буфер не подходят. */
int member_sync_build_owner_msg(uint64_t node_id, const char* tags, uint8_t* out, size_t capacity);