From 00c4065771bf9f0aeb51df01a6553ece54533003 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 27 Oct 2026 02:50:27 +0100 Subject: [PATCH] Use the signed-history limit for manual attachment parsing --- src/chat/chat_msg.c | 18 ++++++++---------- tests/test_chat_attachment.c | 11 +++++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/chat/chat_msg.c b/src/chat/chat_msg.c index de702754..75ee5c0b 100644 --- a/src/chat/chat_msg.c +++ b/src/chat/chat_msg.c @@ -1124,7 +1124,7 @@ void chat_core_attachment_download(struct UTUN_INSTANCE* inst, const char* chann const uint8_t* sig_blob = (const uint8_t*)sqlite3_column_blob(st, 2); int sig_len = sqlite3_column_bytes(st, 2); int64_t author_node_id = sqlite3_column_int64(st, 3); - if (!jdata || jlen <= 0 || !sig_blob || sig_len != 64) { + if (!jdata || jlen <= 0 || jlen > DB_SYNC_MESSAGE_MAX || !sig_blob || sig_len != 64) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "%s: download bad msg data ch=%s id=%lld", CC_ID, channel_id, (long long)msg_id); sqlite3_finalize(st); return; @@ -1134,11 +1134,10 @@ void chat_core_attachment_download(struct UTUN_INSTANCE* inst, const char* chann if (!ds) { DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "manual media download: missing body ch=%s", channel_id); sqlite3_finalize(st); return; } const char* d_start = ds + 5; const char* d_end = strchr(d_start, '"'); - if (!d_end || d_end - d_start > 65536) { + if (!d_end || d_end - d_start > DB_SYNC_MESSAGE_MAX) { DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "manual media download: invalid body length ch=%s", channel_id); sqlite3_finalize(st); return; } - size_t bi = (size_t)(d_end - d_start); char* body = u_malloc(bi + 1); - if (!body) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "manual media download: body allocation failed bytes=%zu", bi); sqlite3_finalize(st); return; } + size_t bi = (size_t)(d_end - d_start); char body[DB_SYNC_MESSAGE_MAX + 1]; memcpy(body, d_start, bi); body[bi] = 0; char content_type[32] = {0}; @@ -1163,16 +1162,16 @@ void chat_core_attachment_download(struct UTUN_INSTANCE* inst, const char* chann if (observed) { DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "manual encrypted download: cannot index manifest ch=%s result=%d", channel_id, observed); if (observed == -1) media_queue_channel_backfill(inst, channel_id); - sqlite3_finalize(st); u_free(body); return; + sqlite3_finalize(st); return; } } int final_rc = sqlite3_finalize(st); if (final_rc != SQLITE_OK) { - DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "manual media download: history finalize failed ch=%s rc=%d", channel_id, final_rc); u_free(body); return; + DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "manual media download: history finalize failed ch=%s rc=%d", channel_id, final_rc); return; } if (encrypted_attachment) { - if (chat_attachment_want(inst, channel_id, message)) { u_free(body); return; } + if (chat_attachment_want(inst, channel_id, message)) return; sqlite3_stmt* encrypted = NULL; int rc = sqlite3_prepare_v2(cc->db, "SELECT cipher_path FROM attachment_keys WHERE channel=? AND message_id=? AND downloaded=1", -1, &encrypted, NULL); @@ -1184,11 +1183,11 @@ void chat_core_attachment_download(struct UTUN_INSTANCE* inst, const char* chann int finish = sqlite3_finalize(encrypted); if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || finish != SQLITE_OK) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "encrypted media: local file lookup failed ch=%s step=%d finalize=%d db=%s", - channel_id, rc, finish, sqlite3_errmsg(cc->db)); u_free(body); return; + channel_id, rc, finish, sqlite3_errmsg(cc->db)); return; } if (present) { if (chat_attachment_downloaded(inst, channel_id, message, 0)) media_queue_channel_backfill(inst, channel_id); - u_free(body); return; + return; } } @@ -1199,7 +1198,6 @@ void chat_core_attachment_download(struct UTUN_INSTANCE* inst, const char* chann DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "%s: manual download ch=%s id=%lld ct=%s file=%s", CC_ID, channel_id, (long long)msg_id, content_type, base_filename); md_start_download(inst, body, bi, channel_id, base_filename, (uint64_t)db_ts, author_sig, (uint64_t)author_node_id, msg_id, content_type); - u_free(body); } static void chat_core_attachment_download_trampoline_impl(void* arg) { diff --git a/tests/test_chat_attachment.c b/tests/test_chat_attachment.c index 4d4d70c1..016f34d0 100644 --- a/tests/test_chat_attachment.c +++ b/tests/test_chat_attachment.c @@ -4,13 +4,16 @@ #include #include #include +#include #include "../src/utun_instance.h" #include "../src/chat/chat_core.h" #include "../src/chat/chat_attachment.h" #include "../src/chat/chat_core_priv.h" #include "../src/chat/chat_event.h" +#include "../src/chat/chat_feedback.h" #include "../src/media_async/media_async.h" #include "../src/media_delivery/media_download.h" +#include "../src/media_delivery/media_index.h" #include "../lib/platform_compat.h" #include "../lib/debug_config.h" #include "../lib/mem.h" @@ -970,7 +973,7 @@ int __wrap_media_download_start(struct UTUN_INSTANCE* inst, uint64_t group, cons } static void test_long_manual_manifest(void) { - struct fixture f; setup(&f); enum { BLOCKS = 30 }; + struct fixture f; setup(&f); enum { BLOCKS = 23 }; uint8_t content[BLOCKS], ids[BLOCKS * 16], signatures[BLOCKS * 64]; struct media_index_result result = {.file_size = BLOCKS, .block_size = 1, .num_blocks = BLOCKS, .block_ids = ids, .block_sigs = signatures}; result.media_id[0] = 99; memset(ids, 0, sizeof(ids)); @@ -993,10 +996,10 @@ static void test_long_manual_manifest(void) { for (unsigned i = 0; i < 64; i++) offset += (size_t)snprintf(body + offset, sizeof(body) - offset, "%02x", signatures[block * 64 + i]); assert(offset < sizeof(body)); } - body[offset] = 0; assert(offset > 4096); + body[offset] = 0; assert(offset > 3000); char json[6500]; int length = snprintf(json, sizeof(json), "{\"n\":%llu,\"ch\":\"%s\",\"ct\":\"application/octet-stream\",\"d\":\"%s\"}", (unsigned long long)f.inst->node_id, f.channel, body); - assert(length > 0 && (size_t)length < sizeof(json)); + assert(length > 0 && length <= DB_SYNC_MESSAGE_MAX && (size_t)length < sizeof(json)); struct DB_SYNC_INSTANCE* si = si_find(f.inst, f.channel); assert(si); uint64_t timestamp = db_sync_next_timestamp(si); struct db_message_revision revision = {.ver = 1}; revision.nonce[0] = 33; uint8_t signature[64], message[32]; @@ -1010,7 +1013,7 @@ static void test_long_manual_manifest(void) { int64_t id = sqlite3_column_int64(st, 0); assert(sqlite3_finalize(st) == SQLITE_OK); expected_download = &result; download_starts = 0; chat_core_attachment_download(f.inst, f.channel, id); assert(download_starts == 1); expected_download = NULL; cleanup(&f); - DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "PASS manual manifest:%zu bytes, all30 signed blocks reach download port; rejected start frees context", offset); + DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "PASS manual manifest:%zu bytes, all23 signed blocks near protocol limit reach download port; rejected start frees context", offset); } int main(void) {