Browse Source
Root cause: etcp_on_up/etcp_on_down/etcp_conn_process_send_queue used fixed buffers (links_str[256], l_status[256]) with snprintf chains in loops over links. With 15+ links (IPv6 addresses), sizeof(buf)-offset turned negative → vsnprintf size=(size_t)(-17) → FORTIFY abort on Bionic. Fixes: - lib/strbuf.h/c: safe growable printf buffer (auto-realloc) - etcp.c: 3 snprintf chains → strbuf - debug_config.c: BUF_ADVANCE macro prevents offset overflow - android_jni_bridge.c: bridge_log va_list fix, set_member_flags guard - chat_core.c: TAGS_ADVANCE macro in tags_commit - AndroidManifest.xml: dataSync→specialUse (time limit exhausted) - stcp: ed25519 pubkey exchange, tcp link addr/status in JNI bridge - etcp_router: encrypted flag + ECDH per peer-pair - node_conn_direct: TCP link support - topo_node: group nodes cleanup - media_delivery: buffer overflow fix - test_stcp: ed25519 key exchange test - tests: invite_group_create + Makefile updatetopo_upd
24 changed files with 597 additions and 126 deletions
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* strbuf.c — safe growable printf buffer |
||||
*/ |
||||
#include "strbuf.h" |
||||
#include "mem.h" |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#define STRBUF_MIN_CAP 16 |
||||
|
||||
void strbuf_init(struct strbuf *sb, size_t initial_cap) { |
||||
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; } |
||||
else { sb->cap = 0; } |
||||
sb->len = 0; |
||||
} |
||||
|
||||
void strbuf_free(struct strbuf *sb) { |
||||
if (sb->buf) { u_free(sb->buf); sb->buf = NULL; } |
||||
sb->cap = 0; sb->len = 0; |
||||
} |
||||
|
||||
char *strbuf_detach(struct strbuf *sb) { |
||||
char *ret = sb->buf; |
||||
sb->buf = NULL; sb->cap = 0; sb->len = 0; |
||||
return ret; |
||||
} |
||||
|
||||
void strbuf_reset(struct strbuf *sb) { |
||||
if (sb->buf) sb->buf[0] = '\0'; |
||||
sb->len = 0; |
||||
} |
||||
|
||||
static int strbuf_grow(struct strbuf *sb, size_t need) { |
||||
size_t nc = sb->cap ? sb->cap : STRBUF_MIN_CAP; |
||||
while (nc < need) { nc *= 2; if (nc < STRBUF_MIN_CAP) return -1; } |
||||
char *n = u_realloc(sb->buf, nc); |
||||
if (!n) return -1; |
||||
sb->buf = n; sb->cap = nc; |
||||
return 0; |
||||
} |
||||
|
||||
int strbuf_addf(struct strbuf *sb, const char *fmt, ...) { |
||||
va_list ap; |
||||
|
||||
/* measure */ |
||||
va_start(ap, fmt); |
||||
int need = vsnprintf(NULL, 0, fmt, ap); |
||||
va_end(ap); |
||||
if (need < 0) { return -1; } |
||||
|
||||
/* grow */ |
||||
size_t want = sb->len + (size_t)need + 1; /* +1 for '\0' */ |
||||
if (want > sb->cap && strbuf_grow(sb, want) < 0) return -1; |
||||
|
||||
/* write */ |
||||
va_start(ap, fmt); |
||||
int w = vsnprintf(sb->buf + sb->len, sb->cap - sb->len, fmt, ap); |
||||
va_end(ap); |
||||
if (w > 0) sb->len += (size_t)w; |
||||
return w; |
||||
} |
||||
|
||||
void strbuf_addc(struct strbuf *sb, char c) { |
||||
if (sb->len + 2 > sb->cap && strbuf_grow(sb, sb->len + 2) < 0) return; |
||||
sb->buf[sb->len++] = c; |
||||
sb->buf[sb->len] = '\0'; |
||||
} |
||||
|
||||
void strbuf_adds(struct strbuf *sb, const char *s) { |
||||
if (!s) return; |
||||
size_t slen = strlen(s); |
||||
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; |
||||
sb->buf[sb->len] = '\0'; |
||||
} |
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* strbuf.h — safe growable printf buffer |
||||
* |
||||
* Replaces snprintf(buf+off, sizeof(buf)-off, ...) chains that crash |
||||
* on overflow (snprintf returns un-truncated size, offset exceeds buffer, |
||||
* next call gets negative size → FORTIFY abort on Bionic/Android). |
||||
* |
||||
* Usage: |
||||
* struct strbuf sb = strbuf_new(); |
||||
* strbuf_addf(&sb, "{\"ver\":\"%d\"", ver); |
||||
* strbuf_addf(&sb, ",\"name\":\"%s\"", name); |
||||
* strbuf_addf(&sb, "}"); |
||||
* // use strbuf_str(&sb) or strbuf_detach(&sb)
|
||||
* strbuf_free(&sb); |
||||
*/ |
||||
#ifndef STRBUF_H |
||||
#define STRBUF_H |
||||
|
||||
#include <stddef.h> |
||||
#include <stdarg.h> |
||||
|
||||
struct strbuf { |
||||
char *buf; /* allocated buffer, always null-terminated */ |
||||
size_t cap; /* total capacity (including '\0') */ |
||||
size_t len; /* current length (excluding '\0') */ |
||||
}; |
||||
|
||||
/* zero-init on stack: struct strbuf sb = {0}; */ |
||||
#define strbuf_new() ((struct strbuf){NULL, 0, 0}) |
||||
|
||||
void strbuf_init(struct strbuf *sb, size_t initial_cap); |
||||
void strbuf_free(struct strbuf *sb); |
||||
|
||||
/* returns buf and detaches — caller must u_free(). sb is reset to empty */ |
||||
char *strbuf_detach(struct strbuf *sb); |
||||
|
||||
/* reset len=0, keep allocation */ |
||||
void strbuf_reset(struct strbuf *sb); |
||||
|
||||
/* printf-like append. auto-grows. returns written bytes or -1 on OOM */ |
||||
int strbuf_addf(struct strbuf *sb, const char *fmt, ...) |
||||
__attribute__((format(printf, 2, 3))); |
||||
|
||||
void strbuf_addc(struct strbuf *sb, char c); |
||||
void strbuf_adds(struct strbuf *sb, const char *s); |
||||
|
||||
/* safe accessor — never returns NULL */ |
||||
#define strbuf_str(sb) ((sb)->buf ? (sb)->buf : "") |
||||
|
||||
#endif /* STRBUF_H */ |
||||
@ -0,0 +1,166 @@
|
||||
/**
|
||||
* @file test_invite_group_create.c |
||||
* @brief Full invite flow: conn_mgr_open_invite → B is member → CONN_EVENT_JOIN |
||||
*/ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdarg.h> |
||||
#include "../lib/platform_compat.h" |
||||
#include "../lib/mem.h" |
||||
#include "../lib/debug_config.h" |
||||
#include "test_utils.h" |
||||
#ifndef _WIN32 |
||||
#include <unistd.h> |
||||
#endif |
||||
|
||||
#include "../src/transport_layer/etcp.h" |
||||
#include "../src/transport_layer/etcp_connections.h" |
||||
#include "../src/config_parser.h" |
||||
#include "../src/config_updater.h" |
||||
#include "../src/utun_instance.h" |
||||
#include "../src/routing_layer/topo_group.h" |
||||
#include "../src/routing_layer/topo_node.h" |
||||
#include "../src/routing_layer/topo_node_sqlite.h" |
||||
#include "../src/routing_layer/conn_mgr.h" |
||||
|
||||
#define TIMEOUT_TB 50000 |
||||
#define POLL_MS 20 |
||||
|
||||
static void fail(const char* msg) { fprintf(stderr, "FAIL: %s\n", msg); fflush(stderr); exit(1); } |
||||
|
||||
static void wf(const char* path, const char* fmt, ...) { |
||||
va_list ap; va_start(ap, fmt); |
||||
FILE* f = fopen(path, "w"); |
||||
if (!f) { fail("fopen"); return; } |
||||
vfprintf(f, fmt, ap); fclose(f); |
||||
va_end(ap); |
||||
} |
||||
|
||||
static char* ls(const char* p, const char* k) { |
||||
char b[1024]; FILE* f = fopen(p, "r"); if (!f) return NULL; |
||||
size_t n = fread(b, 1, sizeof(b) - 1, f); fclose(f); b[n] = 0; |
||||
char* x = strstr(b, k); if (!x) return NULL; |
||||
x += strlen(k) + 1; while (*x == ' ' || *x == '\t') x++; |
||||
char* r = u_strdup(x); char* e = r; while (*e && *e != '\n' && *e != '\r') e++; *e = 0; |
||||
return r; |
||||
} |
||||
|
||||
static int result = 0; |
||||
|
||||
static void ccb(struct CONN_MGR_HANDLE* h, uint64_t nid, uint64_t gid, |
||||
enum conn_mgr_event ev, void* arg) { |
||||
(void)h; (void)gid; (void)arg; |
||||
fprintf(stderr, "CB: ev=%d node=0x%llx\n", (int)ev, (unsigned long long)nid); fflush(stderr); |
||||
if (ev == CONN_EVENT_JOIN) result = 1; |
||||
if (ev == CONN_EVENT_TIMEOUT) result = 2; |
||||
} |
||||
|
||||
static void to_cb(void* arg) { (void)arg; fprintf(stderr, "TIMEOUT\n"); fflush(stderr); result = 2; } |
||||
|
||||
int main(void) { |
||||
debug_config_init(); |
||||
debug_set_level(DEBUG_LEVEL_TRACE); |
||||
debug_enable_file_output("/tmp/test_inv_crash.log", 1); |
||||
utun_instance_set_tun_init_enabled(0); |
||||
|
||||
char tdir[] = "/tmp/utin_XXXXXX"; |
||||
if (test_mkdtemp(tdir) != 0) { fail("mkdtemp"); return 1; } |
||||
char ca[256], cb[256]; |
||||
snprintf(ca, sizeof(ca), "%s/a.conf", tdir); |
||||
snprintf(cb, sizeof(cb), "%s/b.conf", tdir); |
||||
int porta = 52000 + (getpid() % 10000), portb = porta + 1; |
||||
|
||||
wf(ca, "[global]\ntun_ip=10.94.0.1/24\ntun_ifname=tun89\ndb_path=%s\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", tdir, porta); |
||||
wf(cb, "[global]\ntun_ip=10.94.0.2/24\ntun_ifname=tun88\ndb_path=%s\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", tdir, portb); |
||||
config_ensure_keys_and_node_id(ca); config_ensure_keys_and_node_id(cb); |
||||
|
||||
char* rva = ls(ca, "priv"); char* pua = ls(ca, "pub"); char* pub = ls(cb, "pub"); |
||||
wf(ca, "[global]\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.94.0.1/24\ntun_ifname=tun89\ndb_path=%s\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[client: to_b]\nkeepalive=1\npeer_public_key=%s\nlink=s1:127.0.0.1:%d\n[allowed_keys]\nallow_all=1\n", rva, pua, tdir, porta, pub, portb); |
||||
char* rvb = ls(cb, "priv"); |
||||
wf(cb, "[global]\nmy_private_key=%s\nmy_public_key=%s\ntun_ip=10.94.0.2/24\ntun_ifname=tun88\ndb_path=%s\n[server: s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n", rvb, pub, tdir, portb); |
||||
u_free(rva); u_free(pua); u_free(pub); u_free(rvb); |
||||
|
||||
struct UASYNC* ua = uasync_create(); |
||||
struct UTUN_INSTANCE* a = utun_instance_create(ua, ca); |
||||
if (!a) { fail("create A"); goto clean; } |
||||
utun_instance_init(a); |
||||
|
||||
/* ── Add a TCP socket (simulating Android STCP server) ── */ |
||||
{ |
||||
struct TCP_SOCKET* ts = u_calloc(1, sizeof(*ts)); |
||||
ts->instance = a; |
||||
ts->type = CFG_SERVER_TYPE_PUBLIC; |
||||
ts->sock_id = (uint8_t)a->next_socket_id++; |
||||
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); |
||||
sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */ |
||||
sin.sin_port = htons((uint16_t)55555); |
||||
memcpy(&ts->local_addr, &sin, sizeof(sin)); |
||||
ts->interface_addr = ts->local_addr; |
||||
ts->next = a->tcp_sockets; a->tcp_sockets = ts; |
||||
fprintf(stderr, "Added TCP socket ts=%p next=%p\n", (void*)ts, (void*)ts->next); fflush(stderr); |
||||
} |
||||
fprintf(stderr, "tcp_sockets=%p etcp_sockets=%p\n", (void*)a->tcp_sockets, (void*)a->etcp_sockets); fflush(stderr); |
||||
|
||||
/* Get B's node_id and pubkey from its instance */ |
||||
struct UTUN_INSTANCE* b = utun_instance_create(ua, cb); |
||||
if (!b) { fail("create B"); a->running = 0; utun_instance_destroy(a); uasync_destroy(ua, 0); goto clean; } |
||||
utun_instance_init(b); |
||||
|
||||
/* ── Set up B as member of group 0x7777777700000001 ── */ |
||||
{ |
||||
uint64_t gid = 0x7777777700000001ULL; |
||||
char ch_str[32]; snprintf(ch_str, sizeof(ch_str), "%llu", (unsigned long long)gid); |
||||
topo_groups_create_group(b->topo_groups, gid, TOPO_GROUP_TYPE_CHAT, ch_str); |
||||
const char* ch_name = "TestCh"; |
||||
uint8_t sig_msg[256]; size_t slen = 0; |
||||
/* msg: ch_id\0 || name\0 || owner(8 LE) || x25519_pub(32) || ed_pub(32) */ |
||||
size_t ch_id_len = strlen(ch_str) + 1; memcpy(sig_msg + slen, ch_str, ch_id_len); slen += ch_id_len; |
||||
size_t name_len = strlen(ch_name) + 1; memcpy(sig_msg + slen, ch_name, name_len); slen += name_len; |
||||
uint64_t owner = b->node_id; memcpy(sig_msg + slen, &owner, 8); slen += 8; |
||||
memcpy(sig_msg + slen, b->my_keys.public_key, 32); slen += 32; |
||||
memcpy(sig_msg + slen, b->my_ed25519_pubkey, 32); slen += 32; |
||||
uint8_t ch_sig[64]; |
||||
sc_ed25519_sign(b->my_ed25519_privkey, sig_msg, slen, ch_sig); |
||||
topo_node_sqlite_channel_put(b->topo_sqlite_db, ch_str, ch_name, owner, |
||||
b->my_keys.public_key, NULL, |
||||
b->my_ed25519_pubkey, NULL, ch_sig); |
||||
fprintf(stderr, "B: channel setup gid=0x%016llX ch=%s name=%s\n", |
||||
(unsigned long long)gid, ch_str, ch_name); fflush(stderr); |
||||
} |
||||
|
||||
uint64_t nid_b = b->node_id; |
||||
uint8_t pk_b[32]; memcpy(pk_b, b->my_keys.public_key, 32); |
||||
|
||||
/* Build TOPO_NODE for invite */ |
||||
struct TOPO_NODE* ni = u_calloc(1, sizeof(*ni)); |
||||
ni->node_id = nid_b; memcpy(ni->public_key, pk_b, 32); |
||||
struct TOPO_ADDR4* a4 = u_calloc(1, sizeof(*a4)); |
||||
uint8_t ip[4] = {127, 0, 0, 1}; memcpy(a4->addr, ip, 4); |
||||
a4->port = (uint16_t)portb; a4->type = TOPO_ADDR_NAT; a4->protocol = 1; |
||||
ni->v4_addrs = a4; |
||||
|
||||
uint64_t gid = 0x7777777700000001ULL; |
||||
fprintf(stderr, "=== conn_mgr_open_invite gid=0x%016llX nid=0x%016llX ===\n", |
||||
(unsigned long long)gid, (unsigned long long)nid_b); fflush(stderr); |
||||
|
||||
int r = conn_mgr_open_invite(a, gid, ni, nid_b, ccb, NULL, NULL); |
||||
fprintf(stderr, "conn_mgr_open_invite => %d\n", r); fflush(stderr); |
||||
|
||||
void* tt = uasync_set_timeout(ua, TIMEOUT_TB, NULL, to_cb, "to"); |
||||
int el = 0; |
||||
while (!result && el < TIMEOUT_TB + 5000) { uasync_poll(ua, POLL_MS); el += POLL_MS; } |
||||
if (tt) uasync_cancel_timeout(ua, tt); |
||||
|
||||
a->running = 0; utun_instance_destroy(a); |
||||
b->running = 0; utun_instance_destroy(b); |
||||
uasync_destroy(ua, 0); |
||||
u_free(a4); u_free(ni); |
||||
|
||||
fprintf(stderr, "=== DONE result=%d ===\n", result); fflush(stderr); |
||||
|
||||
clean: |
||||
test_unlink(ca); test_unlink(cb); test_rmdir(tdir); |
||||
debug_disable_file_output(); |
||||
return (result == 1) ? 0 : 1; |
||||
} |
||||
Loading…
Reference in new issue