From 21ec740d82ea98fdde943a69fdf61884e62c67f2 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 15 Sep 2026 22:39:27 +0300 Subject: [PATCH] socks_proxy: defer conn free to fix UAF from synchronous write_queue flush --- src/proxy/socks_proxy.c | 22 ++++++++++++++++++---- src/proxy/socks_proxy.h | 1 + 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/proxy/socks_proxy.c b/src/proxy/socks_proxy.c index ee4a822b..0425be41 100644 --- a/src/proxy/socks_proxy.c +++ b/src/proxy/socks_proxy.c @@ -28,6 +28,8 @@ static void on_read_cb(struct ll_queue* q, void* arg); static void on_fin_cb(struct tcp_conn* tc, void* arg); static void on_error_cb(struct tcp_conn* tc, int err, void* arg); static void on_closed_cb(struct tcp_conn* tc, void* arg); +static void socks_proxy_conn_free_deferred(void* arg); +static void socks_proxy_conn_free_soon(struct socks_proxy_conn* c); static void tx_waiter_cb(struct ll_queue* q, void* arg); static void tx_retry_timer_cb(void* arg); static void socks_issue_dns(struct socks_proxy_conn* c, const char* host, uint8_t kind); @@ -119,8 +121,8 @@ static int write_to_client(struct socks_proxy_conn* c, const uint8_t* data, uint } memcpy(buf, data, len); e->dgram = buf; e->len = len; - queue_data_put(c->tc->write_queue, e); c->bytes_to_client += len; + queue_data_put(c->tc->write_queue, e); return 0; } @@ -556,7 +558,7 @@ static void on_error_cb(struct tcp_conn* tc, int err, void* arg) { struct UTUN_INSTANCE* inst = c->inst; uint64_t via = c->via_node_id; uint32_t sid = c->stream_id; - socks_proxy_conn_free(c); + socks_proxy_conn_free_soon(c); if (notify && inst) send_msg(inst, TOPO_GROUP_UTUN, via, TCP_PROXY_SUBCMD_ERROR, sid, NULL, 0, 1); } @@ -565,7 +567,7 @@ static void on_closed_cb(struct tcp_conn* tc, void* arg) { DEBUG_INFO(DEBUG_CATEGORY_PROXY, "socks_proxy: tcp closed sid=%08x state=%d fm_r=%d fm_l=%d rem_cl=%d bytes_client=%u bytes_exit=%u", c->stream_id, c->state, tc->fin_remote, tc->fin_local, c->rem_closed, c->bytes_to_client, c->bytes_from_exit); if (!c->close_sent && !c->close_pending) send_close(c); - socks_proxy_conn_free(c); + socks_proxy_conn_free_soon(c); } // ==================================================================== @@ -627,8 +629,8 @@ int socks_proxy_handle_etcp(struct socks_proxy_conn** head, int* count, uint8_t* buf = memory_pool_alloc(c->tc->data_pool); if (e && buf) { memcpy(buf, data, data_len); e->dgram = buf; e->len = (uint16_t)data_len; - queue_data_put(c->tc->write_queue, e); c->bytes_to_client += (uint32_t)data_len; + queue_data_put(c->tc->write_queue, e); } else { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "socks_proxy: handle_data alloc failed sid=%08x wq=%d bytes_in=%u bytes_out=%u", stream_id, c->tc->write_queue->count, c->bytes_from_exit, c->bytes_to_client); @@ -665,10 +667,22 @@ int socks_proxy_handle_etcp(struct socks_proxy_conn** head, int* count, return 1; } +static void socks_proxy_conn_free_deferred(void* arg) { + struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg; + c->free_soon_id = NULL; + socks_proxy_conn_free(c); +} + +static void socks_proxy_conn_free_soon(struct socks_proxy_conn* c) { + if (!c || c->free_soon_id) return; + c->free_soon_id = uasync_call_soon(c->ua, c, socks_proxy_conn_free_deferred); +} + void socks_proxy_conn_free(struct socks_proxy_conn* c) { if (!c) return; if (c->freed) return; c->freed = 1; + if (c->free_soon_id) { uasync_call_soon_cancel(c->ua, c->free_soon_id); c->free_soon_id = NULL; } struct socks_proxy_conn** head = c->head; int* count = c->count; DEBUG_INFO(DEBUG_CATEGORY_PROXY, "socks_proxy: FREE sid=%08x state=%d total=%d", c->stream_id, c->state, count ? *count : 0); if (c->close_pending && c->inst) { diff --git a/src/proxy/socks_proxy.h b/src/proxy/socks_proxy.h index 871c0660..c4bf6114 100644 --- a/src/proxy/socks_proxy.h +++ b/src/proxy/socks_proxy.h @@ -52,6 +52,7 @@ struct socks_proxy_conn { uint8_t is_http; uint8_t state; uint8_t freed; + void* free_soon_id; // handle отложенного освобождения (uasync_call_soon) uint8_t buf[1024]; uint16_t buf_len; uint8_t* tx_buf; // буфер при backpressure (retry в tx_waiter_cb)