diff --git a/lib/strbuf.c b/lib/strbuf.c index 52c27137..92221415 100644 --- a/lib/strbuf.c +++ b/lib/strbuf.c @@ -11,6 +11,8 @@ #define STRBUF_MIN_CAP 16 void strbuf_init(struct strbuf *sb, size_t initial_cap) { + *sb = strbuf_new(); + if (initial_cap > UINT32_MAX) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: capacity exceeds allocator range=%zu", initial_cap); return; } if (initial_cap < STRBUF_MIN_CAP) initial_cap = STRBUF_MIN_CAP; sb->buf = u_malloc(initial_cap); if (sb->buf) { sb->buf[0] = '\0'; sb->cap = initial_cap; } @@ -35,8 +37,9 @@ void strbuf_reset(struct strbuf *sb) { } static int strbuf_grow(struct strbuf *sb, size_t need) { + if (need > UINT32_MAX) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: size exceeds allocator range=%zu", need); return -1; } size_t nc = sb->cap ? sb->cap : STRBUF_MIN_CAP; - while (nc < need) { nc *= 2; if (nc < STRBUF_MIN_CAP) return -1; } + while (nc < need) { if (nc > UINT32_MAX / 2) { nc = need; break; } nc *= 2; } char *n = u_realloc(sb->buf, nc); if (!n) return -1; sb->buf = n; sb->cap = nc; @@ -51,6 +54,9 @@ int strbuf_addf(struct strbuf *sb, const char *fmt, ...) { int need = vsnprintf(NULL, 0, fmt, ap); va_end(ap); if (need < 0) { return -1; } + if ((size_t)need > UINT32_MAX - 1 - sb->len) { + DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: formatted text exceeds allocator range"); return -1; + } /* grow */ size_t want = sb->len + (size_t)need + 1; /* +1 for '\0' */ @@ -65,6 +71,7 @@ int strbuf_addf(struct strbuf *sb, const char *fmt, ...) { } void strbuf_addc(struct strbuf *sb, char c) { + if (sb->len > UINT32_MAX - 2) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: character exceeds allocator range"); return; } if (sb->len + 2 > sb->cap && strbuf_grow(sb, sb->len + 2) < 0) return; sb->buf[sb->len++] = c; sb->buf[sb->len] = '\0'; @@ -96,6 +103,7 @@ failed: void strbuf_adds(struct strbuf *sb, const char *s) { if (!s) return; size_t slen = strlen(s); + if (slen > UINT32_MAX - 1 - sb->len) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "strbuf: string exceeds allocator range"); return; } if (sb->len + slen + 1 > sb->cap && strbuf_grow(sb, sb->len + slen + 1) < 0) return; memcpy(sb->buf + sb->len, s, slen); sb->len += slen; diff --git a/src/chat/chat_core.c b/src/chat/chat_core.c index 687424bd..d0faca8c 100644 --- a/src/chat/chat_core.c +++ b/src/chat/chat_core.c @@ -528,17 +528,25 @@ overflow: } int chat_core_get_node_name(struct UTUN_INSTANCE* inst, uint64_t node_id, char* out, size_t sz) { - struct chat_core_ctx* cc = CC(inst); - if (!cc || !cc->initialized || !out) return -1; - out[0] = '\0'; + struct chat_core_ctx* cc = inst ? CC(inst) : NULL; + if (out && sz) out[0] = 0; + if (!cc || !cc->initialized || !cc->db || !out || !sz) { + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "node name: invalid context/output node=%016llx", (unsigned long long)node_id); return -1; + } sqlite3_stmt* st = NULL; - if (sqlite3_prepare_v2(cc->db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL) != SQLITE_OK) return -1; - sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); - if (sqlite3_step(st) == SQLITE_ROW) { + int rc = sqlite3_prepare_v2(cc->db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL); + if (rc == SQLITE_OK) rc = sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); + if (rc == SQLITE_OK) rc = sqlite3_step(st); + if (rc == SQLITE_ROW) { const char* n = (const char*)sqlite3_column_text(st, 0); if (n) snprintf(out, sz, "%s", n); + else if (sqlite3_errcode(cc->db) == SQLITE_NOMEM) rc = SQLITE_NOMEM; + } + int final = sqlite3_finalize(st); + if ((rc != SQLITE_ROW && rc != SQLITE_DONE) || final != SQLITE_OK) { + out[0] = 0; DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "node name: lookup node=%016llx rc=%d finalize=%d: %s", + (unsigned long long)node_id, rc, final, sqlite3_errmsg(cc->db)); return -1; } - sqlite3_finalize(st); return 0; } diff --git a/src/chat/chat_status.c b/src/chat/chat_status.c index f9907df6..3859b95a 100644 --- a/src/chat/chat_status.c +++ b/src/chat/chat_status.c @@ -13,6 +13,8 @@ #include "../ntp_time.h" #include "../../lib/ll_queue.h" #include "../../lib/mem.h" +#include "../../lib/strbuf.h" +#include #include "../../lib/platform_compat.h" #include "../transport_layer/etcp.h" #include "../transport_layer/etcp_connections.h" @@ -23,6 +25,9 @@ #include "../routing_layer/topo_group.h" #include +/* Ошибка форматирования/памяти не публикует частичный ответ. */ +#define STATUS_APPEND(...) do { if (strbuf_addf(&text, __VA_ARGS__) < 0 || text.len > INT_MAX) goto error; } while (0) + struct address_profile_text { char text[80]; }; static struct address_profile_text link_address_profile_text(const struct ETCP_LINK *link) { @@ -59,23 +64,13 @@ static struct address_profile_text link_address_profile_text(const struct ETCP_L } static void get_node_name(struct UTUN_INSTANCE* inst, uint64_t node_id, char* out, size_t sz) { - struct chat_core_ctx* cc = CC(inst); - out[0] = '\0'; - if (!cc || !cc->db || node_id == 0) return; - sqlite3_stmt* st = NULL; - if (sqlite3_prepare_v2(cc->db, "SELECT name FROM nodes WHERE node_id=?", -1, &st, NULL) != SQLITE_OK) return; - sqlite3_bind_int64(st, 1, (sqlite3_int64)node_id); - if (sqlite3_step(st) == SQLITE_ROW) { - const char* n = (const char*)sqlite3_column_text(st, 0); - if (n) snprintf(out, sz, "%s", n); - } - sqlite3_finalize(st); + chat_core_get_node_name(inst, node_id, out, sz); } static void chat_core_collect_status(struct UTUN_INSTANCE* inst) { - char buf[8192]; int off = 0; + char buf[96]; int off = 0; struct strbuf text = strbuf_new(); - if (!CC(inst) || !CC(inst)->initialized || !inst) { + if (!inst || !CC(inst) || !CC(inst)->initialized) { off = snprintf(buf, sizeof(buf), "uTun not initialized\n"); chat_event_post(inst, CHAT_EVT_STATUS_REFRESH, (const uint8_t*)buf, off); return; @@ -83,32 +78,32 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) { struct uasync_load load; if (uasync_get_load(inst->ua, &load) == ERR_OK && load.valid) { - off += snprintf(buf + off, sizeof(buf) - off, "uasync CPU: %.1f%%, average %.1f%% (window %.0f ms, age %.0f ms)\n", + STATUS_APPEND( "uasync CPU: %.1f%%, average %.1f%% (window %.0f ms, age %.0f ms)\n", load.current_percent, load.average_percent, load.window_us / 1000.0, (get_time_us() - load.sample_time_us) / 1000.0); - } else off += snprintf(buf + off, sizeof(buf) - off, "uasync CPU: unavailable\n"); + } else STATUS_APPEND( "uasync CPU: unavailable\n"); struct NTP_TIME* ntp = &inst->ntp; - off += snprintf(buf + off, sizeof(buf) - off, "=== NTP ===\n"); - off += snprintf(buf + off, sizeof(buf) - off, "Enabled: %s\n", ntp->enabled ? "yes" : "no"); - off += snprintf(buf + off, sizeof(buf) - off, "Synced: %s\n", ntp->synced ? "yes" : "no"); - off += snprintf(buf + off, sizeof(buf) - off, "Offset: %.1f s\n", ntp->offset_us / 1000000.0); + STATUS_APPEND( "=== NTP ===\n"); + STATUS_APPEND( "Enabled: %s\n", ntp->enabled ? "yes" : "no"); + STATUS_APPEND( "Synced: %s\n", ntp->synced ? "yes" : "no"); + STATUS_APPEND( "Offset: %.1f s\n", ntp->offset_us / 1000000.0); if (ntp->server_count > 0 && ntp->servers) { - off += snprintf(buf + off, sizeof(buf) - off, "Servers: %d\n", ntp->server_count); + STATUS_APPEND( "Servers: %d\n", ntp->server_count); for (int i = 0; i < ntp->server_count && ntp->servers[i]; i++) - off += snprintf(buf + off, sizeof(buf) - off, " [%d] %s%s\n", i, ntp->servers[i], + STATUS_APPEND( " [%d] %s%s\n", i, ntp->servers[i], i == ntp->server_current ? " (current)" : ""); } if (ntp->synced && ntp->last_sync_tb > 0) { uint64_t now_tb = get_time_tb(); uint64_t ago_sec = (now_tb - ntp->last_sync_tb) / 10000; - off += snprintf(buf + off, sizeof(buf) - off, "Last sync: %llu sec ago\n", (unsigned long long)ago_sec); + STATUS_APPEND( "Last sync: %llu sec ago\n", (unsigned long long)ago_sec); } int conn_count = 0; struct ll_entry* entry = inst->connections ? inst->connections->head : NULL; while (entry) { conn_count++; entry = entry->next; } - off += snprintf(buf + off, sizeof(buf) - off, "=== Connections (%d) ===\n", conn_count); + STATUS_APPEND( "=== Connections (%d) ===\n", conn_count); uint64_t my_id = inst->node_id; entry = inst->connections ? inst->connections->head : NULL; @@ -133,11 +128,11 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) { get_node_name(inst, pid, peername, sizeof(peername)); if (peername[0]) - off += snprintf(buf + off, sizeof(buf) - off, "[%s]→[%s] %s ETCP:%s%s(%dL)\n", + STATUS_APPEND( "[%s]→[%s] %s ETCP:%s%s(%dL)\n", myhex, peerhex, peername, conn->links_up ? "UP" : "DOWN", init_str, link_count); else - off += snprintf(buf + off, sizeof(buf) - off, "[%s]→[%s] ETCP:%s%s(%dL)\n", + STATUS_APPEND( "[%s]→[%s] ETCP:%s%s(%dL)\n", myhex, peerhex, conn->links_up ? "UP" : "DOWN", init_str, link_count); @@ -146,7 +141,7 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) { while (link) { char rtt_str[32]; rtt_str[0] = '\0'; if (link->rtt_last > 0) snprintf(rtt_str, sizeof(rtt_str), " / rtt=%ums", link->rtt_last / 10); - off += snprintf(buf + off, sizeof(buf) - off, " LINK#%d: %s %s %s NAT=%s%s\n", + STATUS_APPEND( " LINK#%d: %s %s %s NAT=%s%s\n", link_idx, link->is_tcp ? "TCP" : "UDP", link->conn ? link->conn->name : "?", @@ -159,7 +154,10 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) { entry = entry->next; } - chat_event_post(inst, CHAT_EVT_STATUS_REFRESH, (const uint8_t*)buf, off); + chat_event_post(inst, CHAT_EVT_STATUS_REFRESH, (const uint8_t*)strbuf_str(&text), (int)text.len); + strbuf_free(&text); return; +error: + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "status: formatting/allocation failed bytes=%zu", text.len); strbuf_free(&text); } /* ── Бинарный список соединений: [count:2][entry:46B]* ── */ @@ -167,16 +165,16 @@ static void chat_core_collect_status(struct UTUN_INSTANCE* inst) { #define CONN_LIST_ENTRY_SIZE 46 static void collect_conn_list(struct UTUN_INSTANCE* inst) { - if (!CC(inst) || !CC(inst)->initialized || !inst || !inst->connections) return; + if (!inst || !CC(inst) || !CC(inst)->initialized || !inst->connections) return; uint16_t count = 0; struct ll_entry* entry = inst->connections->head; - while (entry) { count++; entry = entry->next; } - if (count > 250) count = 250; + while (entry && count < 250) { count++; entry = entry->next; } + if (entry) DEBUG_DEBUG(DEBUG_CATEGORY_CHAT_SYNC, "connection list limited to250 entries"); size_t buf_sz = 2 + (size_t)count * CONN_LIST_ENTRY_SIZE; uint8_t* buf = (uint8_t*)u_malloc(buf_sz); - if (!buf) return; + if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "connection list allocation failed bytes=%zu", buf_sz); return; } uint16_t* hdr = (uint16_t*)buf; *hdr = count; @@ -200,14 +198,15 @@ static void collect_conn_list(struct UTUN_INSTANCE* inst) { *p++ = flags; uint8_t lc = 0; - struct ETCP_LINK* tl = conn->links; while (tl) { lc++; tl = tl->next; } + struct ETCP_LINK* tl = conn->links; while (tl && lc < UINT8_MAX) { lc++; tl = tl->next; } *p++ = lc; uint16_t rtt = 0; if (conn->links) rtt = conn->links->rtt_last; memcpy(p, &rtt, 2); p += 2; - uint16_t inflight_kb = (uint16_t)(conn->unacked_bytes / 1024); + uint64_t inflight = conn->unacked_bytes / 1024; + uint16_t inflight_kb = inflight > UINT16_MAX ? UINT16_MAX : (uint16_t)inflight; memcpy(p, &inflight_kb, 2); p += 2; char name[32]; memset(name, 0, 32); @@ -222,9 +221,9 @@ static void collect_conn_list(struct UTUN_INSTANCE* inst) { /* ── Полный текстовый дамп одного соединения ── */ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_id) { - char buf[8192]; int off = 0; + char buf[96]; int off = 0; struct strbuf text = strbuf_new(); - if (!CC(inst) || !CC(inst)->initialized || !inst || !inst->connections) { + if (!inst || !CC(inst) || !CC(inst)->initialized || !inst->connections) { off = snprintf(buf, sizeof(buf), "uTun not initialized\n"); chat_event_post(inst, CHAT_EVT_CONN_METRICS, (const uint8_t*)buf, off); return; @@ -255,7 +254,7 @@ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_ int link_count = 0, links_up_count = 0; struct ETCP_LINK* tl = conn->links; while (tl) { if (tl->link_status) links_up_count++; link_count++; tl = tl->next; } - off += snprintf(buf + off, sizeof(buf) - off, + STATUS_APPEND( "=== PEER: %04llX\u2192%04llX [%s] ===\n" "Role: %s\n" "Status: %s Links: %d/%d Initialized: %s MTU: %d\n" @@ -278,25 +277,25 @@ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_ conn->rx_dup_count, conn->tx_dup_count, conn->next_tx_id, conn->last_rx_id, conn->last_delivered_id, conn->rx_ack_till); - if (conn->input_queue) off += snprintf(buf + off, sizeof(buf) - off, "input_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->input_queue), queue_entry_count(conn->input_queue)); - if (conn->input_send_q) off += snprintf(buf + off, sizeof(buf) - off, "send_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->input_send_q), queue_entry_count(conn->input_send_q)); - if (conn->input_wait_ack) off += snprintf(buf + off, sizeof(buf) - off, "wait_ack: %uB/%dp\n", (unsigned)queue_total_bytes(conn->input_wait_ack), queue_entry_count(conn->input_wait_ack)); + if (conn->input_queue) STATUS_APPEND( "input_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->input_queue), queue_entry_count(conn->input_queue)); + if (conn->input_send_q) STATUS_APPEND( "send_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->input_send_q), queue_entry_count(conn->input_send_q)); + if (conn->input_wait_ack) STATUS_APPEND( "wait_ack: %uB/%dp\n", (unsigned)queue_total_bytes(conn->input_wait_ack), queue_entry_count(conn->input_wait_ack)); unsigned ack_bytes = 0, ack_packets = 0; for (struct ETCP_LINK* link = conn->links; link; link = link->next) { ack_bytes += (unsigned)queue_total_bytes(link->ack_q); ack_packets += queue_entry_count(link->ack_q); } - off += snprintf(buf + off, sizeof(buf) - off, "ack_q: %uB/%up ", ack_bytes, ack_packets); - if (conn->recv_q) off += snprintf(buf + off, sizeof(buf) - off, "recv_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->recv_q), queue_entry_count(conn->recv_q)); - if (conn->output_queue) off += snprintf(buf + off, sizeof(buf) - off, "out_q: %uB/%dp\n", (unsigned)queue_total_bytes(conn->output_queue), queue_entry_count(conn->output_queue)); + STATUS_APPEND( "ack_q: %uB/%up ", ack_bytes, ack_packets); + if (conn->recv_q) STATUS_APPEND( "recv_q: %uB/%dp ", (unsigned)queue_total_bytes(conn->recv_q), queue_entry_count(conn->recv_q)); + if (conn->output_queue) STATUS_APPEND( "out_q: %uB/%dp\n", (unsigned)queue_total_bytes(conn->output_queue), queue_entry_count(conn->output_queue)); - off += snprintf(buf + off, sizeof(buf) - off, "\n--- ACK Debug ---\n" + STATUS_APPEND( "\n--- ACK Debug ---\n" "hit_inf=%u hit_sndq=%u miss=%u link_wait=%u\n", conn->cnt_ack_hit_inf, conn->cnt_ack_hit_sndq, conn->cnt_ack_miss, conn->cnt_link_wait); /* Normalizer */ if (conn->normalizer) { struct PKTNORM* pn = conn->normalizer; - off += snprintf(buf + off, sizeof(buf) - off, + STATUS_APPEND( "\n--- Normalizer ---\n" "in: %llu pkts / %llu B out: %llu pkts / %llu B\n" "frag_size=%u data_ptr=%u data_size=%u alloc_err=%u logic_err=%u\n", @@ -313,7 +312,8 @@ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_ char local_str[54], remote_str[54]; strncpy(local_str, link->conn ? (link->conn->interface_addr.ss_family ? sockaddr_storage_to_str(&link->conn->interface_addr).str : sockaddr_storage_to_str(&link->conn->local_addr).str) : "stcp", sizeof(local_str) - 1); strncpy(remote_str, sockaddr_storage_to_str(&link->remote_addr).str, sizeof(remote_str) - 1); - off += snprintf(buf + off, sizeof(buf) - off, + local_str[sizeof(local_str) - 1] = 0; remote_str[sizeof(remote_str) - 1] = 0; + STATUS_APPEND( "\n--- LINK#%d: %s %s NAT=%s ---\n" "addr: %s -> %s\n" "RTT: %.1f ms TT: %.1f ms\n" @@ -330,7 +330,11 @@ static void collect_conn_metrics(struct UTUN_INSTANCE* inst, uint64_t peer_node_ link = link->next; link_idx++; } - chat_event_post(inst, CHAT_EVT_CONN_METRICS, (const uint8_t*)buf, off); + chat_event_post(inst, CHAT_EVT_CONN_METRICS, (const uint8_t*)strbuf_str(&text), (int)text.len); + strbuf_free(&text); return; +error: + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "connection metrics: formatting/allocation failed peer=%016llx bytes=%zu", + (unsigned long long)peer_node_id, text.len); strbuf_free(&text); } void chat_core_collect_status_trampoline(void* arg) { @@ -381,7 +385,8 @@ int chat_core_parse_member_detail(const uint8_t* data, size_t len, struct chat_m v.hop_count = data[28]; v.link_count = data[29]; v.sock_count = data[30]; size_t expected = CHAT_MEMBER_DETAIL_HEADER_SIZE + (size_t)v.hop_count * 8 + (size_t)v.link_count * CHAT_MEMBER_DETAIL_LINK_SIZE + (size_t)v.sock_count * CHAT_MEMBER_DETAIL_SOCK_SIZE; - if (!v.request_id || !v.group_id || !v.node_id || v.result > CHAT_MEMBER_DETAIL_ERROR || v.transport_flags > 7 || + if (((!v.request_id || !v.group_id || !v.node_id) && v.result != CHAT_MEMBER_DETAIL_INVALID) || + v.result > CHAT_MEMBER_DETAIL_ERROR || v.transport_flags > 7 || v.hop_count > CHAT_MEMBER_DETAIL_MAX_HOPS || v.link_count > CHAT_MEMBER_DETAIL_MAX_LINKS || v.sock_count > CHAT_MEMBER_DETAIL_MAX_SOCKS || expected != len || (v.result && (v.transport_flags || v.own_tcp_active || v.hop_count || v.link_count || v.sock_count))) { @@ -445,7 +450,12 @@ static void collect_member_detail(const struct chat_member_detail_req* req) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "member detail: query request=%llu rc=%d: %s", (unsigned long long)req->request_id, rc, sqlite3_errmsg(CC(inst)->db)); } - sqlite3_finalize(st); + int final = sqlite3_finalize(st); + if (final != SQLITE_OK) { + buf[24] = CHAT_MEMBER_DETAIL_ERROR; + DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "member detail: finalize request=%llu rc=%d: %s", + (unsigned long long)req->request_id, final, sqlite3_errmsg(CC(inst)->db)); + } } if (buf[24]) { DEBUG_WARN(DEBUG_CATEGORY_CHAT_SYNC, "member detail: rejected request=%llu group=%016llx node=%016llx result=%u",