Browse Source

stcp: фикс UAF log_dump, дренаж очередей при закрытии, защита pre-closed серверной ветки, логирование тихих сбросов rx

proxy
evgeny 6 days ago
parent
commit
3f7b4351cd
  1. 7
      src/transport_layer/stcp.c
  2. 42
      src/transport_layer/stcp_link.c
  3. 2
      src/transport_layer/stcp_server.c
  4. 18
      tools/chatgui-android/app/src/main/java/com/utun/chat/data/RingtonePlayer.kt

7
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);

42
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);
}

2
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);
}

18
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) ── */

Loading…
Cancel
Save