You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
13 KiB
253 lines
13 KiB
======================================================================== |
|
uTun3 AUDIT FINDINGS — Сводный отчёт |
|
======================================================================== |
|
|
|
Аудит от 14.05.2026 — обход основных модулей |
|
(use-after-free, out-of-bounds, утечки, reentrancy) |
|
|
|
======================================================================== |
|
HIGH (19 проблем) |
|
======================================================================== |
|
|
|
1. u_async.c:137-165 use-after-free |
|
Частичный успех u_realloc в socket_array_add_internal: если часть |
|
реаллоков прошла, а часть нет — старые массивы уже освобождены, |
|
новые не выделены, sa->sockets/sa->fd_to_index/sa->index_to_fd |
|
указывают на освобождённую память. |
|
|
|
2. u_async.c:842,1167 логическая ошибка |
|
process_posted_tasks никогда не вызывается на Linux. |
|
В epoll-пути: drain_wakeup_pipe не вызывает handle_wakeup/process_posted_tasks. |
|
В poll-пути: wakeup-слот (i==0) обрабатывается с drain_wakeup_pipe и continue, |
|
минуя зарегистрированный коллбэк. |
|
Следствие: uasync_post из другого потока не доставляет задачи — posted_tasks |
|
накапливаются бесконечно. |
|
|
|
3. u_async.c:1383,1398,1418 утечка памяти |
|
При сбое создания wakeup (pipe/socket) в uasync_create не освобождаются: |
|
ua->sockets, ua->timeout_heap, ua->timeout_pool, ua->epoll_fd. |
|
|
|
4. memory_pool.c:51-69 double-free |
|
Нет детекта double-free. Повторный free создаёт цикл во freelist, |
|
memory_pool_alloc выдаёт один и тот же блок двум потребителям. |
|
|
|
5. etcp.c:679-683 утечка памяти |
|
input_queue_cb: при исчерпании inflight_pool вызывается |
|
queue_entry_free(in_pkt) без queue_dgram_free(&in_pkt->ll). |
|
dgram из data_pool утекает. |
|
|
|
6. etcp_connections.c:87,876-926 use-after-free |
|
burst_resp_timer не отменяется в etcp_link_close. |
|
Коллбэк burst_resp_timeout_cb срабатывает на освобождённом link. |
|
|
|
7. etcp_connections.c:846-854 use-after-free |
|
stats_timer не отменяется при провале insert_link в etcp_link_new. |
|
link_stats_timer_cb срабатывает на освобождённом link. |
|
|
|
8. etcp_connections.c:879-886 use-after-free |
|
Ветка conn==NULL в etcp_link_close пропускает отмену ВСЕХ таймеров |
|
(stats_timer, init_timer, shaper_timer, keepalive_timer, burst_resp_timer). |
|
Любой из них сработает на освобождённом link. |
|
|
|
9. etcp_connections.c:1388,1531,1561 out-of-bounds read |
|
INIT request парсинг: проверка pkt_len < 12 должна быть pkt_len < 26 |
|
(23 байта заголовка). Пакет с длиной 12-25 пройдёт проверку, |
|
но читает за границей расшифрованных данных. |
|
|
|
10. pkt_normalizer.c:33 null deref |
|
if (!pn) pn->alloc_errors++ — разыменование NULL после u_calloc. |
|
|
|
11. pkt_normalizer.c:300 null deref |
|
pn_buf_renew в цикле without проверки возврата → memcpy(NULL, ...) |
|
при сбое memory_pool_alloc. |
|
|
|
12. eim_nat.c:80-93 логическая ошибка |
|
Переполнение uint16_t порта: next_port с 65535→0, затем порты |
|
ниже port_start выделяются (включая порт 0). Соединения нерабочие. |
|
|
|
13. eim_nat.c (весь файл) логическая ошибка |
|
Нет механизма истечения/удаления NAT-записей. Таблица навсегда |
|
исчерпывается при долгой работе, eim_nat_egress начинает отказывать. |
|
|
|
14. route_bgp.c:749,762-766 утечка памяти |
|
route_bgp_process_nodeinfo: при пересоздании nodeinfo1 старая |
|
очередь paths сохраняется в локальную переменную, но затем |
|
перезаписывается новой queue_new() — утечка. |
|
|
|
15. route_bgp.c:904-907 утечка памяти |
|
route_bgp_send_nodeinfo: при провале etcp_send не освобождаются |
|
p (u_malloc) и e (queue_entry_new). |
|
|
|
16. route_ping.c:126-134 утечка памяти |
|
route_ping_cancel_for_conn: nat_check_arg (p->arg, u_calloc) не |
|
освобождается. В нормальном и таймаутном путях коллбэк освобождает, |
|
но cancel-путь коллбэк не вызывает. |
|
|
|
17. ll_queue.c:321,439 reentrancy |
|
Синхронный вызов коллбэка при первом queue_data_put (count==1) |
|
без отложенного возобновления через uasync → рекурсивный |
|
ack_timeout_check с устаревшим указателем current. |
|
|
|
18. secure_channel.c:379,711 потенциальный OOB write |
|
sc_encrypt пишет в caller buffer без параметра capacity. |
|
Контракт API опасен: вызывающий должен сам гарантировать размер буфера. |
|
|
|
19. secure_channel.c:432,756 потенциальный OOB write |
|
sc_decrypt — аналогично, нет проверки capacity выходного буфера. |
|
|
|
|
|
======================================================================== |
|
MEDIUM (18 проблем) |
|
======================================================================== |
|
|
|
20. u_async.c:591-639 use-after-free |
|
Stale handle в uasync_cancel_timeout/uasync_call_soon_cancel. |
|
Нет инвалидации (generation counter), пул может переиспользовать память. |
|
|
|
21. memory_pool.c:8,38,61,95 race condition |
|
Глобальная g_total_free изменяется без блокировок, общая для всех пулов. |
|
|
|
22. memory_pool.c:51-69 потенциальный OOB write |
|
Нет проверки принадлежности блока пулу. Free в чужой пул вызывает |
|
memset с чужим object_size — частичная очистка или запись за границу. |
|
|
|
23. timeout_heap.c:43-45 утечка / несоответствие документации |
|
Документация обещает free() при free_callback==NULL, код этого не делает. |
|
|
|
24. etcp.c:718-763 reentrancy |
|
Вложенный ack_timeout_check (через input_send_q_cb→etcp_request_pkt→ |
|
wait_ack_cb) обрабатывает не тот entry, т.к. outer loop использует |
|
current, который мог быть перемещён/удалён внутренним вызовом. |
|
|
|
25. etcp.c:747,944 утечка / дрейф счётчиков |
|
Непроверенный queue_data_put_with_index: при провале счётчики |
|
(retransmissions_count, inflight_bytes) не откатываются. |
|
|
|
26. etcp_connections.c:1731,1763 out-of-bounds read |
|
INIT_RESPONSE парсинг: нужно >=19 байт, проверяется только >=3. |
|
|
|
27. etcp_connections.c:748-753 use-after-free + бесконечный цикл |
|
etcp_socket_remove: при link->conn==NULL remove_link пропускается, |
|
num_channels не уменьшается, цикл перечитывает освобождённый conn->links[0]. |
|
|
|
28. etcp_connections.c:1736-1738 логическая ошибка |
|
link_status перезаписывается значением remote_keepalive, игнорируя |
|
локальный recv_keepalive. Временно помечает link UP даже когда |
|
локальный приём мёртв. |
|
|
|
29. pkt_normalizer.c:43 integer overflow/underflow |
|
frag_size = mtu - ACK_REZERV - UDP_HDR_SIZE - UDP_SC_HDR_SIZE. |
|
При mtu < 158 беззнаковое вычитание даёт ~65379 → огромные буферы. |
|
|
|
30. pkt_normalizer.c:240 утечка памяти |
|
При провале queue_data_put фрагмент и dgram не освобождаются. |
|
|
|
31. eim_nat.c:97,173 null deref |
|
Нет проверки ctx->initialized/ctx->table в egress/ingress. |
|
|
|
32. eim_nat.c:268,293 логическая ошибка |
|
Статические пробросы (port forward) вне port range недоступны. |
|
|
|
33. secure_channel.c:66 key hygiene |
|
Нет sc_free_ctx — сессионный ключ не зануляется при уничтожении контекста. |
|
|
|
34. secure_channel.c:322,620 key hygiene |
|
ECDH shared secret остаётся на стеке незанулённым после |
|
sc_derive_session_key. |
|
|
|
35. secure_channel.c:352,680 nonce entropy |
|
При постоянном провале random_bytes seed остаётся 0 (static init). |
|
Nonce становится полностью предсказуемым (counter+timestamp). |
|
|
|
36. route_bgp.c:1122 утечка памяти |
|
route_bgp_send_nat_info: etcp_send без проверки → pkt/e утекают при провале. |
|
|
|
37. route_bgp.c:1157 утечка памяти (аналогично #36) |
|
route_bgp_send_nat_check_req: etcp_send без проверки. |
|
|
|
38. config_parser.c:1055-1066 логическая ошибка |
|
update_config_keys дописывает [global] секцию в конец файла. |
|
При повторном вызове получается дубликат секции, парсер может сбойнуть. |
|
|
|
|
|
======================================================================== |
|
LOW (13 проблем) |
|
======================================================================== |
|
|
|
39. u_async.c:1594-1617 утечка (abort) |
|
abort() в destroy до освобождения posted_tasks и мьютекса. |
|
|
|
40. u_async.c:1608 race condition |
|
posted_tasks_head читается без posted_lock в destroy. |
|
|
|
41. memory_pool.c:47 truncation |
|
size_t → uint32_t в u_calloc_impl (object_size > UINT32_MAX). |
|
|
|
42. memory_pool.h:16 signedness |
|
int free_count может обернуться при двойном освобождении. |
|
|
|
43. timeout_heap.c:31 dead code |
|
freed_count никогда не обновляется, timeout_heap_get_freed_count не реализован. |
|
|
|
44. timeout_heap.c:66-67 integer overflow |
|
capacity*2 и sizeof(TimeoutEntry)*new_cap могут переполниться. |
|
|
|
45. ll_queue.c:106-127 dangling pointer |
|
queue_free освобождает hash_table, но не чистит entry->hash_next. |
|
|
|
46. pkt_normalizer.c:374 десинхронизация потока |
|
При провале ll_alloc_lldgram 2 байта заголовка уже consumed, |
|
но фрагмент сброшен → потеря данных. |
|
|
|
47. eim_nat.c:105,181 unaligned access (ARM) |
|
*(uint16_t*)(ip_data + 6) — невыровненный доступ на ARM. |
|
|
|
48. secure_channel.c:108-131 избыточность |
|
CRC32 поверх CCM (аутентифицированное шифрование) избыточен. |
|
|
|
49. secure_channel.h:51-52 dead code |
|
Неиспользуемые поля send_nonce[13]/recv_nonce[13]. |
|
|
|
50. config_parser.c:312 undefined behavior |
|
~0U << (32 - cidr) при cidr==0 — сдвиг на ширину типа. |
|
|
|
51. route_ping.c:227 integer overflow |
|
uint16_t avg_rtt = timeout_ms * 10 — переполнение при timeout_ms > 6553. |
|
|
|
|
|
======================================================================== |
|
Файлы без ошибок |
|
======================================================================== |
|
|
|
- route_node.c — чисто |
|
- tun_if.c — чисто |
|
- etcp_api.c — только порядок queue_entry_free/dgram_free (LOW, см. etcp.c Issue #7 старого аудита) |
|
- etcp_loadbalancer.c — не аудирован (250 строк) |
|
|
|
|
|
======================================================================== |
|
Рекомендуемый порядок исправления |
|
======================================================================== |
|
|
|
1. КРИТИЧЕСКИ ФУНКЦИОНАЛЬНЫЕ: |
|
u_async.c #2 (posted_tasks не работают на Linux) |
|
u_async.c #3 (утечки при сбое старта) |
|
|
|
2. USE-AFTER-FREE: |
|
etcp_connections.c #6,#7,#8,#5 (висячие таймеры) |
|
u_async.c #1 (частичный realloc) |
|
ll_queue.c #17 (reentrancy) |
|
|
|
3. УТЕЧКИ ПАМЯТИ: |
|
etcp.c #5 (dgram из data_pool) |
|
route_bgp.c #14,#15,#36,#37 |
|
route_ping.c #16 |
|
|
|
4. OOB / NULL DEREF: |
|
etcp_connections.c #9,#26 (INIT bounds) |
|
pkt_normalizer.c #10,#11 (null deref) |
|
|
|
5. ЛОГИКА: |
|
eim_nat.c #12,#13 (порты + expiry) |
|
memory_pool.c #4 (double-free) |
|
config_parser.c #38 (дубликат секции)
|
|
|