From 3f7b4351cdad81e010533813daa13a68bc5649a6 Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 25 Sep 2026 13:49:43 +0300 Subject: [PATCH] =?UTF-8?q?stcp:=20=D1=84=D0=B8=D0=BA=D1=81=20UAF=20log=5F?= =?UTF-8?q?dump,=20=D0=B4=D1=80=D0=B5=D0=BD=D0=B0=D0=B6=20=D0=BE=D1=87?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B4=D0=B5=D0=B9=20=D0=BF=D1=80=D0=B8=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82=D0=B8=D0=B8,=20=D0=B7?= =?UTF-8?q?=D0=B0=D1=89=D0=B8=D1=82=D0=B0=20pre-closed=20=D1=81=D0=B5?= =?UTF-8?q?=D1=80=D0=B2=D0=B5=D1=80=D0=BD=D0=BE=D0=B9=20=D0=B2=D0=B5=D1=82?= =?UTF-8?q?=D0=BA=D0=B8,=20=D0=BB=D0=BE=D0=B3=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B8=D1=85=D0=B8=D1=85=20?= =?UTF-8?q?=D1=81=D0=B1=D1=80=D0=BE=D1=81=D0=BE=D0=B2=20rx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/transport_layer/stcp.c | 7 ++-- src/transport_layer/stcp_link.c | 42 +++++++++++++++---- src/transport_layer/stcp_server.c | 2 +- .../java/com/utun/chat/data/RingtonePlayer.kt | 18 +++++++- 4 files changed, 56 insertions(+), 13 deletions(-) diff --git a/src/transport_layer/stcp.c b/src/transport_layer/stcp.c index 0c7de7b8..634334ae 100644 --- a/src/transport_layer/stcp.c +++ b/src/transport_layer/stcp.c @@ -364,11 +364,12 @@ void stcp_tx_queue_cb(struct ll_queue *q, void *arg) { // ====== rx backpressure ====== void stcp_rx_push(struct stcp_conn *c, uint8_t *data, size_t len) { - if (!c || !c->rx_queue) return; + if (!c) return; + if (!c->rx_queue) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "stcp_rx_push: no rx_queue, drop %zu bytes", len); return; } struct ll_entry *e = queue_entry_new(0); - if (!e) return; + if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_rx_push: queue_entry_new failed, drop %zu bytes", len); return; } e->dgram = u_malloc(len ? len : 1); - if (!e->dgram) { queue_entry_free(e); return; } + if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_rx_push: dgram malloc(%zu) failed, drop", len); queue_entry_free(e); return; } if (len) memcpy(e->dgram, data, len); e->len = (uint16_t)len; queue_data_put(c->rx_queue, e); diff --git a/src/transport_layer/stcp_link.c b/src/transport_layer/stcp_link.c index c6a87ab8..6d1387b5 100644 --- a/src/transport_layer/stcp_link.c +++ b/src/transport_layer/stcp_link.c @@ -263,6 +263,19 @@ struct stcp_link *stcp_link_connect(struct ETCP_LINK *etcp_link, return link; } +/* Освобождение очереди с предварительным дренажем записей: queue_free не + * освобождает элементы (они должны извлекаться отдельно), иначе записи и их + * dgram-буферы утекают при обрыве с данными в полёте. */ +static void stcp_drain_free_queue(struct ll_queue *q) { + if (!q) return; + struct ll_entry *e; + while ((e = queue_data_get(q)) != NULL) { + queue_dgram_free(e); + queue_entry_free(e); + } + queue_free(q); +} + static void stcp_link_close_impl(void *arg) { struct stcp_link *link = (struct stcp_link *)arg; callring_put(CR_SLINK_IMPL, (uintptr_t)link, (uintptr_t)(link->conn)); @@ -270,19 +283,26 @@ static void stcp_link_close_impl(void *arg) { if (link->cli) { struct stcp_conn *c = stcp_client_get_conn(link->cli); if (!link->conn_pre_closed) { - if (c && c->rx_queue) queue_free(c->rx_queue); - if (link->tx_queue) queue_free(link->tx_queue); + if (c && c->rx_queue) { stcp_drain_free_queue(c->rx_queue); c->rx_queue = NULL; } + if (link->tx_queue) { stcp_drain_free_queue(link->tx_queue); link->tx_queue = NULL; } stcp_client_destroy(link->cli); } else { - if (link->saved_rx_queue) queue_free(link->saved_rx_queue); - if (link->tx_queue) queue_free(link->tx_queue); + if (link->saved_rx_queue) { stcp_drain_free_queue(link->saved_rx_queue); link->saved_rx_queue = NULL; } + if (link->tx_queue) { stcp_drain_free_queue(link->tx_queue); link->tx_queue = NULL; } u_free(link->cli); } } else if (link->conn) { - struct stcp_conn *conn = link->conn; - if (conn->rx_queue) { queue_free(conn->rx_queue); conn->rx_queue = NULL; } - if (link->tx_queue) { queue_free(link->tx_queue); link->tx_queue = NULL; } - stcp_conn_do_close(conn, 0); + if (!link->conn_pre_closed) { + struct stcp_conn *conn = link->conn; + if (conn->rx_queue) { stcp_drain_free_queue(conn->rx_queue); conn->rx_queue = NULL; } + if (link->tx_queue) { stcp_drain_free_queue(link->tx_queue); link->tx_queue = NULL; } + stcp_conn_do_close(conn, 0); + } else { + /* conn уже закрыт и освобождается отложенно — не трогаем link->conn, + * работаем с сохранённой rx_queue. */ + if (link->saved_rx_queue) { stcp_drain_free_queue(link->saved_rx_queue); link->saved_rx_queue = NULL; } + if (link->tx_queue) { stcp_drain_free_queue(link->tx_queue); link->tx_queue = NULL; } + } link->conn = NULL; } DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_link_close_impl: link=%p cli=%p conn=%p conn_pre_closed=%d", (void*)link, (void*)link->cli, (void*)link->conn, link->conn_pre_closed); @@ -314,6 +334,12 @@ void stcp_link_close(struct stcp_link *link) { link->saved_rx_queue = c->rx_queue; c->rx_queue = NULL; link->conn_pre_closed = 1; } } + } else if (link->conn) { + struct stcp_conn *c = link->conn; + if (c->hs_timer) { uasync_cancel_timeout(link->ua, c->hs_timer); c->hs_timer = NULL; } + if (c->state == STCP_STATE_CLOSED || c->state == STCP_STATE_ERROR) { + link->saved_rx_queue = c->rx_queue; c->rx_queue = NULL; link->conn_pre_closed = 1; + } } uasync_call_soon(link->ua, link, stcp_link_close_impl); } diff --git a/src/transport_layer/stcp_server.c b/src/transport_layer/stcp_server.c index 1dc6d7e2..13914d12 100644 --- a/src/transport_layer/stcp_server.c +++ b/src/transport_layer/stcp_server.c @@ -162,6 +162,7 @@ static void server_hs_phase2_cb(struct stcp_conn *c, uint8_t *data, size_t len) } log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_server hs_resp AFTER xor", enc_dst, STCP_HS_ENC_SERVER); for (int i = 0; i < padding; i++) resp[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER + i] = (uint8_t)(salt2[0] ^ i); + log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_ETCP, "stcp_server FULL RESP", resp, total_resp); int r = stcp_try_send(c, resp, total_resp); if (r < 0) { u_free(resp); stcp_conn_do_close(c, 3); return; } @@ -176,7 +177,6 @@ static void server_hs_phase2_cb(struct stcp_conn *c, uint8_t *data, size_t len) } c->state = STCP_STATE_DATA; DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: handshake OK, entering DATA state"); - log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_ETCP, "stcp_server FULL RESP", resp, total_resp); if (c->on_ready) c->on_ready(c, c->ready_arg); stcp_recv_set(c, 0, 1, server_data_cb); } diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt index aa6ebbd1..6bdd2259 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt @@ -9,6 +9,8 @@ import android.media.Ringtone import android.media.RingtoneManager import android.net.Uri import android.os.Build +import android.os.Handler +import android.os.Looper import android.os.VibrationEffect import android.os.Vibrator import com.utun.chat.ChatApplication @@ -36,6 +38,17 @@ class RingtonePlayer { private var vibrator: Vibrator? = null private var focusRequest: AudioFocusRequest? = null + /* Вибрация идёт конечными one-shot (repeat=-1), пере-запускаемыми таймером из приложения: + на ряде OEM-чипов (sc27xx и пр.) бесконечный repeat=0 живёт в HAL и не гасится cancel(). */ + private val vibrateHandler = Handler(Looper.getMainLooper()) + private val vibrateRunnable = object : Runnable { + override fun run() { + if (vibrator == null) return + vibrator?.vibrate(VibrationEffect.createWaveform(longArrayOf(0, 1000), -1)) + vibrateHandler.postDelayed(this, 2000) + } + } + fun play() { stop() val ctx = ChatApplication.instance @@ -130,8 +143,9 @@ class RingtonePlayer { } ?: return if (!v.hasVibrator()) return try { - v.vibrate(VibrationEffect.createWaveform(longArrayOf(1000, 1000), 0)) vibrator = v + vibrateHandler.removeCallbacks(vibrateRunnable) + vibrateRunnable.run() LogManager.addLog("INFO", "Ringtone", "vibration started") } catch (e: Exception) { LogManager.addLog("WARN", "Ringtone", "vibrate failed: ${e.message}") @@ -141,6 +155,8 @@ class RingtonePlayer { private fun stopVibrate() { vibrator?.let { try { it.cancel() } catch (_: Exception) {} } vibrator = null + vibrateHandler.removeCallbacks(vibrateRunnable) + LogManager.addLog("INFO", "Ringtone", "vibration stopped") } /* ── audio focus (fallback API 26–27) ── */